Skip to content

Reworked symbolic execution#10

Closed
gustavo-grieco wants to merge 80 commits intomasterfrom
fixed-sym-exec
Closed

Reworked symbolic execution#10
gustavo-grieco wants to merge 80 commits intomasterfrom
fixed-sym-exec

Conversation

@gustavo-grieco
Copy link
Owner

@gustavo-grieco gustavo-grieco commented May 1, 2025

This PR brings a good number of important changes to the symbolic execution code:

  • It uses the a more recent hevm version, which improved a lot in the past months regarding the precision and efficiency of the symbolic execution exploration
  • It removes the concolic execution: this code should be moved to hevm directly, since it is too hard to maintain from our side, and we don´t know exactly when it is useful.
  • It adds a new type of test result: unsolvable. This new value is used to signal that a symbolic exploration finished to review all possible states without any counter example to trigger an assertion failure.
  • It improves the full symbolic execution of a single transaction, as this seems to be the best approach to explore the code using symbolic execution, some notable changes:
    • A corpus is shuffled before selecting a sequence for the symbolic exploration
    • Bitwulza is the new default smt solver, but other solvers can be selected
    • Support for handling symbolic block.number and block.timestamp
    • Support for avoiding doing symbolic exploration for useless (e.g. no arguments) or unsupported inputs (e.g. dynamic arguments)
    • Two modes for using symbolic execution: verification mode (stateless) and exploration (fuzzing+sym-exec)
    • Two strategies are used for introducing symbolic transactions in the exploration mode: (1) a random transaction is inserted at a random position and (2) the last transaction of a sequence is symbolized.
    • Support for RPC query when doing symbolic exploration (including cache that will be queried but not updated)
    • Added some more tests for symbolic execution (verification mode)

Some unrelated changes included in this PR:

  • Removed etheno related code (obsolete)
  • Removed tests related with gas usage (covered by hevm per opcode) and self-destruction (no longer relevant in the current fork)
  • Removed usage of very old solc version in the tests

Known issues:

  • UI needs to be adapted in verification mode since the symbolic worker is busy and it can block the refresh of the UI. Text mode is advised.
  • There is a detection to some cases where hevm produces a counter example, but it is not reproduced correctly. This is a bug that will be eventually solved upstream, but if we manage to detect a counter example that does not increase coverage (e.g. reached an assertion), echidna will trigger a failure so it can be reported upstream. This detection is not perfect, and there cases where it does not properly work.

Summary by CodeRabbit

  • New Features

    • Added symbolic verification mode with method-level verification and selectable SMT solvers.
    • Introduced configuration options for SMT solver choice and maximum symbolic exploration states.
    • Tests can now be marked as "verified" when formally proven unsolvable.
    • UI and JSON output enhanced to display and support the "verified" test status.
    • Added random list shuffling utility for improved test input randomness.
  • Bug Fixes

    • Improved symbolic execution error handling and event reporting.
    • Fixed test case handling and refined configuration parsing.
  • Refactor

    • Restructured symbolic execution logic to separate exploration and verification modes.
    • Simplified worker and event types; removed gas tracking from internal components.
    • Removed deprecated symbolic execution module and related functions.
  • Chores

    • Updated test suites and configuration files to align with new symbolic execution features.
    • Enhanced CI workflow with updated Solidity compiler versions.
    • Cleaned up codebase by removing deprecated functions, modules, and imports.

@coderabbitai
Copy link

coderabbitai bot commented May 1, 2025

Walkthrough

This update restructures symbolic execution and verification in the Echidna smart contract testing framework. It removes the legacy symbolic execution module, replacing it with a modular design featuring new symbolic exploration and verification modules. Configuration and test state handling are updated, including new YAML options and a "verified" test state. Worker-related types and logic are refactored into a dedicated module. Numerous supporting modules and tests are adjusted to reflect these changes.

Changes

File(s) / Path(s) Change Summary
lib/Echidna/SymExec.hs Removed legacy symbolic execution module and all its exports.
lib/Echidna/SymExec/Common.hs
lib/Echidna/SymExec/Exploration.hs
lib/Echidna/SymExec/Verification.hs
Added new modules for symbolic execution: common utilities, exploration, and verification logic.
lib/Echidna/SymExec/Symbolic.hs Renamed module from Echidna.Symbolic to Echidna.SymExec.Symbolic.
lib/Echidna/Campaign.hs Refactored symbolic execution logic: new verification mode, new shrinking/exploration loop, updated APIs.
lib/Echidna/Worker.hs
lib/Echidna/Types/Worker.hs
Introduced new worker/event types and logic; moved relevant functions/types from Campaign.
lib/Echidna/Types/Campaign.hs Removed worker/event types; added SMT solver and exploration options; updated comments and constants.
lib/Echidna/Config.hs
lib/Echidna/Types/Config.hs
Updated config parsing for new solver/exploration fields; moved event type import.
lib/Echidna/Exec.hs Simplified transaction execution functions; removed gas tracking from signatures and logic.
lib/Echidna/Mutator/Corpus.hs Changed selectFromCorpus to use the Corpus type directly.
lib/Echidna/Transaction.hs Updated imports to new symbolic execution module; no logic changes.
lib/Echidna/Solidity.hs
lib/Echidna.hs
Removed Etheno initialization logic; updated world construction for assertion signatures.
lib/Echidna/Types/World.hs Added assertSigs field to World.
lib/Echidna/Types/Test.hs Added Unsolvable test state, assertion helpers, and updated JSON logic.
lib/Echidna/Types/Random.hs Added list shuffling functions.
lib/Echidna/Types/Cache.hs New module for contract and slot cache type aliases.
lib/Echidna/Output/JSON.hs Added "verified" status; removed gas info from campaign JSON.
lib/Echidna/UI/Report.hs
lib/Echidna/UI/Widgets.hs
Added "verified" test state handling in UI; removed gas info display.
lib/Echidna/UI.hs Added gas consumption rate tracking in UI.
lib/Echidna/Events.hs
lib/Echidna/Onchain.hs
lib/Echidna/SourceMapping.hs
lib/Echidna/Test.hs
lib/Echidna/Types/Tx.hs
Updated imports to new symbolic execution module.
lib/Echidna/Output/Corpus.hs Updated event push import to new worker module.
lib/Echidna/Server.hs Updated event handling for new symbolic execution events.
package.yaml Updated version, dependencies (asyncarray, added unliftio-core).
.github/workflows/ci.yml Updated Solidity compiler test matrix to newer versions.
src/test/Common.hs Added verified function; updated for new World structure; removed gas helpers.
src/test/Tests/Integration.hs
src/test/Tests/Research.hs
src/test/Tests/Assertion.hs
src/test/Tests/Cheat.hs
src/test/Tests/Dapptest.hs
src/test/Tests/Seed.hs
src/test/Tests/Values.hs
Updated imports and test lists for new worker/event types and test state.
src/test/Tests/Symbolic.hs Replaced symbolic tests with new verification-based contract; updated for "verified" state.
tests/solidity/basic/default.yaml
tests/solidity/symbolic/verify.yaml
Updated config options: removed concolic/gas/init flags, added SMT solver and exploration options.
tests/solidity/symbolic/sym.sol
tests/solidity/symbolic/sym-assert.sol
Removed legacy symbolic test contracts.
tests/solidity/symbolic/verify.sol Added new contract with assertion-based verification targets.
tests/solidity/symbolic/explore.sol
tests/solidity/symbolic/explore.yaml
Added new contract and config for symbolic exploration.
tests/solidity/research/harvey.yaml Added research test config for test/shrink limits.
tests/solidity/symbolic/sym.yaml Deleted legacy symbolic YAML config.
lib/Echidna/SymExec/Common.hs Added symbolic execution utilities: symbolic VM setup, model interpretation, caching, and exploration.
lib/Echidna/SymExec/Exploration.hs Added symbolic execution exploration module for method targeting and asynchronous exploration.
lib/Echidna/SymExec/Verification.hs Added symbolic execution verification module for method verification with concurrency and solver management.
lib/Echidna/Types/Worker.hs New module defining worker types, events, stop reasons, and JSON serialization.
lib/Echidna/Worker.hs New module with worker event handling, JSON instances, event pushing, and pretty-printing.
lib/Echidna/Types/Random.hs Added Fisher-Yates shuffle implementation for lists.
lib/Echidna/UI.hs Added gas consumption rate tracking with GasTracker state and updated status line output.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Worker
    participant SymbolicEngine
    participant SMT_Solver

    User->>Worker: Start (config: symbolic verification)
    Worker->>SymbolicEngine: verifyMethods()
    loop For each contract method
        SymbolicEngine->>SMT_Solver: Symbolic verification query
        alt Counterexample found
            SMT_Solver-->>SymbolicEngine: Counterexample (Tx)
            SymbolicEngine->>Worker: SymExecError event
        else No bug found
            SMT_Solver-->>SymbolicEngine: No counterexample
            SymbolicEngine->>Worker: SymExecLog event
        end
    end
    Worker->>User: SymbolicVerificationDone
Loading
sequenceDiagram
    participant User
    participant Worker
    participant SymbolicEngine
    participant SMT_Solver

    User->>Worker: Start (config: symbolic exploration)
    Worker->>SymbolicEngine: exploreContract(target method)
    SymbolicEngine->>SMT_Solver: Symbolic execution queries
    alt New coverage found
        SMT_Solver-->>SymbolicEngine: Counterexample(s)
        SymbolicEngine->>Worker: NewCoverage event
    else No new coverage
        SMT_Solver-->>SymbolicEngine: No new states
        SymbolicEngine->>Worker: SymExecLog event
    end
    Worker->>User: SymbolicExplorationDone
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit hops through code anew,
Symbolic dreams in modules grew.
Old concolic trails now swept,
"Verified!" the tests have leapt.
Workers scurry, states are tracked,
SMTs and shuffles neatly packed.
In coverage fields, the bunnies cheer—
Symbolic spring has blossomed here! 🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01dcc8e and 80dcc54.

📒 Files selected for processing (4)
  • lib/Echidna/Types/Cache.hs (1 hunks)
  • lib/Echidna/Types/Config.hs (2 hunks)
  • tests/solidity/basic/killed.sol (0 hunks)
  • tests/solidity/basic/killed.yaml (0 hunks)
💤 Files with no reviewable changes (2)
  • tests/solidity/basic/killed.yaml
  • tests/solidity/basic/killed.sol
✅ Files skipped from review due to trivial changes (1)
  • lib/Echidna/Types/Config.hs
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using `error` to terminate the program when encountering unexpected ABI types in `modelToTx` is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build Echidna on windows-latest
  • GitHub Check: Build Echidna on macos-13
  • GitHub Check: Build macOS (aarch64) binary
  • GitHub Check: Build Linux (x86_64) binary
  • GitHub Check: Build macOS (x86_64) binary
  • GitHub Check: Build Echidna on ubuntu-latest
  • GitHub Check: Build Linux (aarch64) binary
🔇 Additional comments (3)
lib/Echidna/Types/Cache.hs (3)

1-5: LGTM! Clean module structure.

The module declaration and imports are well-organized and minimal, importing only the necessary dependencies for the type definitions.


7-7: Well-designed cache type.

The ContractCache type appropriately uses Maybe Contract to represent both cached contracts and cache misses, following standard caching patterns.


8-9: Excellent cache design patterns.

Both cache types follow consistent design principles:

  • SlotCache effectively models per-contract storage with nested maps and optional values
  • ContractNameCache provides clean identifier-to-name mapping
  • Consistent use of Maybe types for handling cache misses across all cache definitions
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fixed-sym-exec

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@gustavo-grieco gustavo-grieco marked this pull request as ready for review June 2, 2025 14:09
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (6)
lib/Echidna/Transaction.hs (1)

158-163: LGTM: New comprehensive transaction regeneration function.

The regenTx function provides a useful complement to mutateTx by mutating all ABI call arguments instead of just one. This more aggressive mutation strategy aligns well with the enhanced symbolic execution framework mentioned in the PR objectives.

Consider adding a comment to clarify the difference between mutateTx (mutates single argument) and regenTx (mutates all arguments) for future maintainers.

lib/Echidna/Campaign.hs (1)

157-175: Innovative dual-strategy symbolic execution approach.

The implementation provides two complementary strategies:

  1. When symExecTargets is set: focuses on random prefixes only
  2. Otherwise: explores both the last transaction and random prefixes

This approach effectively balances targeted and exploratory symbolic execution.

Minor clarification needed: The comment on line 163 states "Discard the last tx, which should be the one increasing coverage", but ltx is actually used on line 175. Consider updating the comment to clarify that it's separated, not discarded.

-    -- Discard the last tx, which should be the one increasing coverage
+    -- Separate the last tx, which should be the one increasing coverage
     let (itxs, ltx) = (init txs, last txs)
lib/Echidna/SymExec.hs (4)

59-63: Consider adding parentheses for clarity.

The boolean logic is correct but would be more readable with explicit parentheses to clarify the precedence of operations.

 suitableForSymExec :: Method -> Bool
-suitableForSymExec m = not $ null m.inputs 
-  && null (filter (\(_, t) -> abiKind t == Dynamic) m.inputs) 
-  && not (T.isInfixOf "_no_symexec" m.name)
+suitableForSymExec m = not (null m.inputs)
+  && null (filter (\(_, t) -> abiKind t == Dynamic) m.inputs) 
+  && not (T.isInfixOf "_no_symexec" m.name)

115-116: Address or remove the TODO comment.

The TODO suggests switching to AbstractBase but notes it might affect concolic execution. Since concolic execution has been removed, this concern may no longer be valid.

Would you like me to investigate the implications of switching to AbstractBase and create an issue to track this enhancement?


120-122: Remove commented debugging code.

These commented print statements should be removed to keep the codebase clean.

-        --liftIO $ mapM_ print $ checkResults results
         let txs = mapMaybe (modelToTx dst vm.block.timestamp vm.block.number method conf.solConf.sender defaultSender) results
-        --liftIO $ print $ map (runReaderT mempty $ ppTx vm True) txs
         pure txs
       liftIO $ putMVar resultChan $ concat res
-      --liftIO $ print "done"

Also applies to: 125-125


234-238: Consider documenting the zero-check behavior.

The logic correctly handles unconstrained symbolic values (which default to 0) by setting the diff to 0. Consider adding a comment to clarify this behavior for future maintainers.

+        -- If symbolic values are unconstrained (0), no delay is applied
         newTimestamp = fromMaybe 0 $ Map.lookup (Var "symbolic_block_timestamp") cex.vars
         diffTimestamp = if newTimestamp == 0 then 0 else newTimestamp - forceLit oldTimestamp

         newNumber = fromMaybe 0 $ Map.lookup (Var "symbolic_block_number") cex.vars
         diffNumber = if newNumber == 0 then 0 else newNumber - forceLit oldNumber
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd36c5d and 76d51ab.

📒 Files selected for processing (22)
  • flake.nix (1 hunks)
  • lib/Echidna/ABI.hs (2 hunks)
  • lib/Echidna/Campaign.hs (5 hunks)
  • lib/Echidna/Config.hs (1 hunks)
  • lib/Echidna/Events.hs (2 hunks)
  • lib/Echidna/Exec.hs (4 hunks)
  • lib/Echidna/Mutator/Corpus.hs (1 hunks)
  • lib/Echidna/Solidity.hs (2 hunks)
  • lib/Echidna/SourceAnalysis/Slither.hs (1 hunks)
  • lib/Echidna/SourceMapping.hs (2 hunks)
  • lib/Echidna/SymExec.hs (3 hunks)
  • lib/Echidna/Transaction.hs (2 hunks)
  • lib/Echidna/Types/Cache.hs (1 hunks)
  • lib/Echidna/Types/Campaign.hs (3 hunks)
  • lib/Echidna/Types/World.hs (1 hunks)
  • package.yaml (1 hunks)
  • src/Main.hs (1 hunks)
  • src/test/Common.hs (1 hunks)
  • stack.yaml (1 hunks)
  • tests/solidity/basic/default.yaml (1 hunks)
  • tests/solidity/symbolic/sym-assert.yaml (0 hunks)
  • tests/solidity/symbolic/sym.yaml (0 hunks)
💤 Files with no reviewable changes (2)
  • tests/solidity/symbolic/sym.yaml
  • tests/solidity/symbolic/sym-assert.yaml
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Build Echidna on ubuntu-latest
  • GitHub Check: Build Echidna on windows-latest
  • GitHub Check: Build macOS (aarch64) binary
  • GitHub Check: Build Linux (x86_64) binary
  • GitHub Check: Build macOS (x86_64) binary
  • GitHub Check: Build Echidna on macos-13
🔇 Additional comments (29)
flake.nix (1)

55-56: Ensure Nix fetch source checksum is valid for updated revision

The sha256 was updated for the new rev, but please verify that the checksum (sha256-RNT6ySisP1mdR0NJ1Yyu12A4duzsFCfya+l4CNXHlgQ=) matches the actual content of 5f48aaa93e18cf8f42b46109c04a76234b746dc6. An incorrect hash will break the Nix build cache and fetch.
Also, consider aligning this rev with the one used in stack.yaml for coherent hevm versioning.

lib/Echidna/SourceMapping.hs (2)

14-14: LGTM: Import updated for hevm API migration.

The import correctly switches from EVM.Types to EVM.Expr for the simplified literal extraction function.


84-84:

Details

✅ Verification successful

LGTM: Function call updated for hevm API migration.

The replacement of maybeLitByte with maybeLitByteSimp is consistent with the hevm upgrade. The usage pattern suggests the function signatures are compatible.

Let me verify that all usages of the old function have been updated across the codebase:


🏁 Script executed:

#!/bin/bash
# Description: Verify that all old literal extraction functions have been updated
# Expected: No remaining usages of maybeLitByte, maybeLitWord found

echo "Checking for remaining usages of old literal extraction functions..."
rg -n "maybeLitByte\b" --type hs
rg -n "maybeLitWord\b" --type hs

echo "Checking for new simplified functions..."
rg -n "maybeLitByteSimp\b" --type hs
rg -n "maybeLitWordSimp\b" --type hs

Length of output: 742


All old literal extraction functions successfully replaced

  • No remaining uses of maybeLitByte or maybeLitWord found.
  • New calls to maybeLitByteSimp exist in:
    • lib/Echidna/SourceMapping.hs (import at line 14, usage at line 84)
  • New calls to maybeLitWordSimp exist in:
    • lib/Echidna/Events.hs (import at line 20, usage at line 41)

LGTM—migration to the updated hevm API is complete and consistent.

lib/Echidna/Events.hs (2)

20-20: LGTM: Import added for hevm API migration.

The import correctly adds maybeLitWordSimp from EVM.Expr for the simplified literal extraction function.


41-41: LGTM: Function call updated for hevm API migration.

The replacement of maybeLitWord with maybeLitWordSimp completes the consistent API migration pattern across both files. The usage with fromJust suggests the function signatures are compatible.

package.yaml (1)

60-60: LGTM: Dependency change aligns with symbolic execution enhancements.

The addition of list-shuffle dependency supports the corpus shuffling functionality mentioned in the PR objectives, which is part of the enhanced symbolic execution approach.

lib/Echidna/Types/World.hs (1)

20-20:

Details

✅ Verification successful

Verify all World construction sites include the new field.

The addition of assertSigs field is well-structured and aligns with the symbolic execution enhancements. Please ensure all World construction sites have been updated to include this new field.


🏁 Script executed:

#!/bin/bash
# Description: Verify all World construction sites include the assertSigs field
# Expected: All World constructors should include the new assertSigs field

# Search for World constructor usage patterns
ast-grep --pattern 'World { $$$ }'

# Also search for World record construction patterns
rg -A 10 -B 2 "World\s*\{" --type hs

Length of output: 1329


🏁 Script executed:

#!/bin/bash
# Verify mkWorld definition includes the new assertSigs field

# Locate mkWorld implementation with surrounding context
rg -n "mkWorld" -C5 --type hs

Length of output: 2045


All World constructions include assertSigs

The World record in lib/Echidna/Solidity.hs (lines 355–361) and the mkWorld function (lines 346–351) both initialize the new assertSigs field. No missing initializations were found—no further changes are needed.

src/Main.hs (1)

159-161: LGTM: Idiomatic function composition style.

The stylistic change to use function composition with NE.fromList is more idiomatic Haskell and doesn't affect the parsing logic.

lib/Echidna/Config.hs (1)

107-107:

Details

✅ Verification successful

Verify the default value and data type consistency.

The addition of symExecMaxExplore configuration aligns with the symbolic execution enhancements and removal of concolic execution. Please ensure the defaultSymExecMaxExplore constant is properly defined and the CampaignConf data type includes this field.


🏁 Script executed:

#!/bin/bash
# Description: Verify defaultSymExecMaxExplore is defined and CampaignConf includes symExecMaxExplore
# Expected: Both the default constant and the data type field should exist

# Search for the default constant definition
rg "defaultSymExecMaxExplore" --type hs

# Search for CampaignConf data type definition to verify the field exists
ast-grep --pattern 'data CampaignConf = CampaignConf { $$$ }'

# Search for symExecMaxExplore field in data types
rg "symExecMaxExplore\s*::" --type hs

Length of output: 4599


Configuration integration validated

  • defaultSymExecMaxExplore is defined in lib/Echidna/Types/Campaign.hs as an Integer (value 10).
  • CampaignConf in lib/Echidna/Types/Campaign.hs includes symExecMaxExplore :: Integer.
  • lib/Echidna/Config.hs correctly parses "symExecMaxExplore" with the default.

All checks pass—no further changes needed.

lib/Echidna/Solidity.hs (2)

350-350: LGTM! Proper extraction of assertion signatures.

The extraction of assertSigs using filterResults follows the same pattern as other signature extractions in this function and correctly processes the assertion information from Slither.


359-359: LGTM! Consistent addition to World record.

The assertSigs field is properly included in the World record construction, maintaining consistency with the other extracted signature fields.

src/test/Common.hs (1)

157-157: LGTM! Correct update to World constructor.

The addition of empty list arguments properly aligns the test code with the updated World data type that now includes the assertSigs field. Using empty lists as placeholders in test setup is appropriate.

lib/Echidna/SourceAnalysis/Slither.hs (1)

118-118: LGTM! Improved idiomatic expression.

The change from (catMaybes . concat) to concatMap catMaybes is functionally equivalent but more idiomatic and readable. This is a positive style improvement.

lib/Echidna/Types/Cache.hs (1)

1-7: LGTM! Clean and well-designed cache types.

This new module provides clear, purpose-built type aliases for caching in symbolic execution:

  • ContractCache appropriately maps addresses to optional contracts
  • SlotCache uses a logical nested map structure for storage slot caching
  • The module is minimal with appropriate imports and clear naming

These types will likely improve symbolic execution performance through strategic caching.

lib/Echidna/Mutator/Corpus.hs (2)

4-4: LGTM: Import cleanup and abstraction improvement.

The qualified import for Data.Set and addition of Echidna.Types.Corpus align well with the type signature update below.

Also applies to: 10-10


65-68: LGTM: Type signature abstraction improvement.

The change from Set (Int, [Tx]) to Corpus provides better abstraction while maintaining the same underlying functionality. The implementation correctly uses Set.toDescList which suggests Corpus is still backed by a Set internally.

tests/solidity/basic/default.yaml (2)

107-107: LGTM: Configuration comments updated consistently.

The removal of symExecConcolic conditions from these comments aligns with the PR objective to remove concolic execution support. The options now apply whenever symExec is true, which simplifies the configuration model.

Also applies to: 110-110


115-117: LGTM: New symbolic exploration limit configuration.

The addition of symExecMaxExplore provides useful control over the number of states to explore during symbolic execution, which aligns with the PR's focus on improving symbolic execution efficiency.

lib/Echidna/Transaction.hs (1)

208-208: LGTM: Consistent literal wrapping for block number.

Wrapping the block number calculation with Lit ensures consistency with how block numbers are handled elsewhere in the codebase and maintains proper type safety.

lib/Echidna/ABI.hs (2)

194-194: LGTM: Code simplification without logic change.

The removal of redundant parentheses improves readability while maintaining the same mathematical calculation for the signed integer range.


355-358: LGTM: Comprehensive ABI call mutation function.

The mutateAllAbiCall function provides a useful complement to mutateAbiCall by mutating every argument instead of just one random argument. The implementation correctly uses traverse and mapM to apply mutations across all arguments, supporting the more thorough mutation strategy needed for enhanced symbolic execution.

lib/Echidna/Types/Campaign.hs (2)

50-67: LGTM! Clear documentation and sensible configuration changes.

The comment updates accurately reflect the removal of concolic execution support, and the new symExecMaxExplore field provides useful control over symbolic execution exploration limits.


207-211: Reasonable default values for symbolic execution parameters.

The default values are well-chosen:

  • defaultSymExecMaxExplore = 10 provides a reasonable exploration limit
  • Reducing defaultSymExecMaxIters from 10 to 5 helps balance exploration depth with performance
lib/Echidna/Exec.hs (2)

30-31: Correct integration with updated EVM execution interface.

The explicit passing of defaultConfig to exec and exec1 functions ensures consistent execution environment across the refactored symbolic execution framework.

Also applies to: 244-244, 293-293


343-343: Consistent literal representation for block numbers.

Wrapping the block number with Lit maintains consistency with similar changes across the codebase.

lib/Echidna/Campaign.hs (2)

29-31: Good implementation of randomized corpus exploration.

The asynchronous corpus shuffling before symbolic execution helps diversify the exploration and avoid getting stuck in local patterns. The empty corpus check prevents unnecessary work.

Also applies to: 117-130


180-180: Simplified createSymTx interface aligns with new architecture.

The removal of explicit configuration parameters from createSymTx suggests it now uses environment-based configuration, which is consistent with the broader refactoring.

lib/Echidna/SymExec.hs (2)

9-11: Good use of the MonadReader pattern.

The refactoring to use MonadReader for accessing the environment is a clean design that improves testability and reduces the need to pass configuration explicitly through function calls.


252-268: Well-implemented cache layer.

The cachedOracle function provides an efficient caching mechanism that checks local caches before making RPC calls, which should improve performance during symbolic execution.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (4)
lib/Echidna/SymExec.hs (4)

69-69: Remove or uncomment the assertSigs check.

The commented out code {- methodSig elem assertSigs &&-} creates ambiguity about the intended behavior. Either remove it entirely or uncomment it if the check is needed.


102-102: Limited exploration scope - only one method is explored.

The code currently limits symbolic exploration to only the first method after shuffling with take 1 shuffleMethods. This seems overly restrictive given that the configuration includes maxExplore parameter. Consider using the configuration value or documenting why only one method is explored.

-      res <- forM (take 1 shuffleMethods) $ \method -> do
+      res <- forM (take (fromIntegral $ fromMaybe 1 maxExplore) shuffleMethods) $ \method -> do

169-169: Fix typos in function names.

The function names have typos: addBlockConstrains should be addBlockConstraints and senderContraints should be senderConstraints.

-addBlockConstraints :: W256 -> W256 -> Block -> [Prop] -> [Prop]
-addBlockConstraints maxTimestampDiff maxNumberDiff block cs =
+addBlockConstraints :: W256 -> W256 -> Block -> [Prop] -> [Prop]
+addBlockConstraints maxTimestampDiff maxNumberDiff block cs =

Also update the call site:

-                          & #constraints %~ (++ constraints ++ (senderConstraints conf.solConf.sender))
+                          & #constraints %~ (++ constraints ++ (senderConstraints conf.solConf.sender))

175-175: Fix typos in function names.

The function names have typos: addBlockConstrains should be addBlockConstraints and senderContraints should be senderConstraints.

-senderConstraints :: Set Addr -> [Prop]
-senderConstraints as = [foldr (\a b -> POr b (PEq (SymAddr "caller") (LitAddr a))) (PBool False) $ Set.toList as]
+senderConstraints :: Set Addr -> [Prop]
+senderConstraints as = [foldr (\a b -> POr b (PEq (SymAddr "caller") (LitAddr a))) (PBool False) $ Set.toList as]
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76d51ab and cb34d9b.

📒 Files selected for processing (3)
  • flake.nix (1 hunks)
  • lib/Echidna/Campaign.hs (6 hunks)
  • lib/Echidna/SymExec.hs (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • flake.nix
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/Echidna/Campaign.hs
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Build macOS (aarch64) binary
  • GitHub Check: Build Linux (x86_64) binary
  • GitHub Check: Build macOS (x86_64) binary
  • GitHub Check: Build Echidna on ubuntu-latest
  • GitHub Check: Build Echidna on windows-latest
  • GitHub Check: Build Echidna on macos-13
🔇 Additional comments (5)
lib/Echidna/SymExec.hs (5)

9-11: Good monadic abstraction improvement.

The transition from concrete IO types to monadic constraints (MonadIO m, MonadThrow m, MonadReader Env m) provides better composability and testability. This aligns well with the overall refactoring objectives.


59-62: Well-designed filtering predicate.

The suitableForSymExec function provides clear criteria for method eligibility:

  • Methods must have inputs
  • No dynamic ABI types (which are harder to handle symbolically)
  • Explicit opt-out via naming convention

This filtering approach is practical and maintainable.


255-270: Well-implemented caching strategy.

The cachedOracle function provides an efficient caching layer that falls back to the original oracle when cache misses occur. The pattern matching on different query types is clean and the cache lookup logic is correct.


171-173:

Details

❓ Verification inconclusive

Verify constraint bounds for symbolic block values.

The constraints for symbolic block timestamp and number use strict inequalities (PGT, PLT) which could potentially exclude edge cases. Consider if these should be inclusive bounds (PGEq, PLEq) depending on the intended semantics.


🏁 Script executed:

#!/bin/bash
# Search for similar constraint patterns in the codebase to verify consistency
rg -A 3 -B 3 "PGT.*block|PLT.*block" --type hs

Length of output: 688


Verify inclusive/exclusive bounds for symbolic block values

The current addBlockConstraints in lib/Echidna/SymExec.hs (around lines 171–173) uses strict inequalities, which exclude the exact boundary values:

  PGT  (Var "symbolic_block_timestamp")    (block.timestamp)
, PLT  (Sub (Var "symbolic_block_timestamp") (block.timestamp)) $ Lit maxTimestampDiff
, PGT  (Var "symbolic_block_number")       (block.number)
, PLT  (Sub (Var "symbolic_block_number")    (block.number))    $ Lit maxNumberDiff

• If you intend to allow equality at the boundaries (i.e. permit symbolic_block_timestamp == block.timestamp or a difference equal to maxTimestampDiff), consider switching to PGEq and PLEq.
• Otherwise, keep the strict checks as-is and confirm this matches your specification.


96-96:

Details

❓ Verification inconclusive

Verify Bitwuzla solver selection.

The code switches from Z3 to Bitwuzla as mentioned in the PR objectives. Ensure this change is intentional and that Bitwuzla provides the expected performance improvements for your use case.


🌐 Web query:

What are the performance differences between Bitwuzla and Z3 solvers for symbolic execution?

💡 Result:

Bitwuzla and Z3 are both prominent SMT (Satisfiability Modulo Theories) solvers utilized in symbolic execution, each with distinct performance characteristics.

Performance in SMT Competitions:

In the SMT-COMP 2024, Bitwuzla demonstrated superior performance in the QF_Bitvec division, leading in both sequential and parallel tracks. It solved 10,489 benchmarks, outperforming other solvers in the same category. (smt-comp.github.io)

Benchmark Comparisons:

A comparative study involving Bitwuzla and Z3 on various benchmarks revealed that Bitwuzla often outperforms Z3, especially in scenarios involving fixed-size bit-vectors and arrays. For instance, in the "Towards Symbolic Pointers Reasoning in Dynamic Symbolic Execution" study, Bitwuzla solved certain benchmarks more efficiently than Z3. (ar5iv.org)

Considerations for Symbolic Execution:

The choice between Bitwuzla and Z3 for symbolic execution should consider the specific requirements of the application, including the theories involved, the complexity of the constraints, and the need for incremental solving. Bitwuzla's architecture is optimized for bit-vector and array theories, making it suitable for applications in hardware verification and related fields. Z3, being a more general-purpose solver, offers broader support for various theories and may be advantageous in applications requiring extensive theory support.

In summary, while Bitwuzla often outperforms Z3 in benchmarks related to bit-vectors and arrays, the optimal choice depends on the specific context and requirements of the symbolic execution task.

Citations:


Verify Bitwuzla vs. Z3 for your workload
Based on SMT-COMP 2024¹ and recent benchmark studies², Bitwuzla leads in QF_BV and bit-vector/array‐heavy tasks, while Z3 offers broader theory support and robust incremental solving. Make sure your symbolic execution constraints align with Bitwuzla’s strengths before committing to this switch:

• File: lib/Echidna/SymExec.hs, line 96
• Current call:

withSolvers Bitwuzla (fromIntegral conf.campaignConf.symExecNSolvers) 1 timeoutSMT $ \solvers -> 

• Action:
– Confirm that your use case is bit-vector/array-focused and benefits from Bitwuzla’s optimizations.
– If you rely on other theories or heavy incremental solving, benchmark both solvers in your environment.

Citations:

  1. SMT-COMP 2024 QF_BV results: https://smt-comp.github.io/2024/results/qf_bv-single-query/
  2. “Towards Symbolic Pointers Reasoning in Dynamic Symbolic Execution”: https://ar5iv.org/html/2109.03698

Comment on lines +236 to +241
newTimestamp = fromMaybe 0 $ Map.lookup (Var "symbolic_block_timestamp") cex.vars
diffTimestamp = if newTimestamp == 0 then 0 else newTimestamp - forceLit oldTimestamp

newNumber = fromMaybe 0 $ Map.lookup (Var "symbolic_block_number") cex.vars
diffNumber = if newNumber == 0 then 0 else newNumber - forceLit oldNumber

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle potential arithmetic underflow in delay computation.

The delay computation could result in underflow if newTimestamp or newNumber is less than the old values. Consider adding bounds checking or using safe arithmetic operations.

-        diffTimestamp = if newTimestamp == 0 then 0 else newTimestamp - forceLit oldTimestamp
-        diffNumber = if newNumber == 0 then 0 else newNumber - forceLit oldNumber
+        diffTimestamp = if newTimestamp == 0 then 0 else max 0 (newTimestamp - forceLit oldTimestamp)
+        diffNumber = if newNumber == 0 then 0 else max 0 (newNumber - forceLit oldNumber)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
newTimestamp = fromMaybe 0 $ Map.lookup (Var "symbolic_block_timestamp") cex.vars
diffTimestamp = if newTimestamp == 0 then 0 else newTimestamp - forceLit oldTimestamp
newNumber = fromMaybe 0 $ Map.lookup (Var "symbolic_block_number") cex.vars
diffNumber = if newNumber == 0 then 0 else newNumber - forceLit oldNumber
newTimestamp = fromMaybe 0 $ Map.lookup (Var "symbolic_block_timestamp") cex.vars
- diffTimestamp = if newTimestamp == 0 then 0 else newTimestamp - forceLit oldTimestamp
+ diffTimestamp = if newTimestamp == 0 then 0 else max 0 (newTimestamp - forceLit oldTimestamp)
newNumber = fromMaybe 0 $ Map.lookup (Var "symbolic_block_number") cex.vars
- diffNumber = if newNumber == 0 then 0 else newNumber - forceLit oldNumber
+ diffNumber = if newNumber == 0 then 0 else max 0 (newNumber - forceLit oldNumber)
🤖 Prompt for AI Agents
In lib/Echidna/SymExec.hs around lines 236 to 241, the current delay computation
subtracts old values from newTimestamp and newNumber without checking if new
values are smaller, risking arithmetic underflow. To fix this, add bounds
checking before subtraction to ensure the result is never negative, or use safe
arithmetic functions that handle underflow gracefully, returning zero or a
minimum bound instead of a negative value.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
lib/Echidna/SymExec.hs (2)

69-69: Remove commented code to improve clarity.

This is a duplicate of a previous review comment. The commented out assertion check {- methodSig elem assertSigs &&-} creates ambiguity about the intended behavior.


237-241: Handle potential arithmetic underflow in delay computation.

This is a duplicate of a previous review comment. The delay computation could result in underflow if newTimestamp or newNumber is less than the old values.

-        diffTimestamp = if newTimestamp == 0 then 0 else newTimestamp - forceLit oldTimestamp
-        diffNumber = if newNumber == 0 then 0 else newNumber - forceLit oldNumber
+        diffTimestamp = if newTimestamp == 0 then 0 else max 0 (newTimestamp - forceLit oldTimestamp)
+        diffNumber = if newNumber == 0 then 0 else max 0 (newNumber - forceLit oldNumber)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb34d9b and f934990.

📒 Files selected for processing (2)
  • flake.nix (1 hunks)
  • lib/Echidna/SymExec.hs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • flake.nix
🧰 Additional context used
🧠 Learnings (1)
lib/Echidna/SymExec.hs (1)
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.202Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Build Echidna on windows-latest
  • GitHub Check: Build Echidna on ubuntu-latest
  • GitHub Check: Build Echidna on macos-13
  • GitHub Check: Build Linux (x86_64) binary
  • GitHub Check: Build macOS (x86_64) binary
  • GitHub Check: Build macOS (aarch64) binary
🔇 Additional comments (6)
lib/Echidna/SymExec.hs (6)

93-93: LGTM! Fixed undefined rpcInfo issue.

Good fix from the previous review - rpcInfo is now properly initialized instead of being set to undefined.


102-102: Single method exploration is intentional for UX.

Based on retrieved learnings, the limitation to exploring only one method at a time using take 1 shuffleMethods is intentional to improve user experience by providing faster symbolic execution results.


255-271: Well-designed cache-aware oracle implementation.

The cachedOracle function provides an elegant solution for consulting contract and slot caches before falling back to the default oracle. The pattern matching on query types is clean and the cache lookup logic is correct.


54-57: LGTM! Good monadic abstraction.

The refactoring to use MonadIO m, MonadThrow m, MonadReader Env m constraints instead of concrete types improves testability and modularity. The function signature is now more flexible and follows Haskell best practices.


59-70: Excellent filtering logic for symbolic execution suitability.

The new filtering functions suitableForSymExec and filterTarget provide comprehensive logic for determining which methods should undergo symbolic execution. The checks for:

  • Non-empty inputs
  • No dynamic ABI types
  • Exclusion of methods with "_no_symexec" in the name
  • Target-specific filtering

are all well-reasoned and improve the precision of symbolic execution.


96-96: Good solver choice: Bitwuzla over Z3.

The switch from Z3 to Bitwuzla aligns with the PR objectives of using more efficient and precise solving for symbolic execution.

maxExplore = Just (fromIntegral conf.campaignConf.symExecMaxExplore)
askSmtIters = conf.campaignConf.symExecAskSMTIters
rpcInfo = rpcFetcher conf.rpcUrl (fromIntegral <$> conf.rpcBlock)
defaultSender = fromJust $ fmap (.dst) tx <|> Set.lookupMin conf.solConf.sender <|> Just 0
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid unsafe fromJust usage.

The fromJust call could cause a runtime exception if all the alternatives in the <|> chain return Nothing. Consider providing a safe default value instead.

-  let defaultSender = fromJust $ fmap (.dst) tx <|> Set.lookupMin conf.solConf.sender <|> Just 0
+  let defaultSender = fromMaybe 0 $ fmap (.dst) tx <|> Set.lookupMin conf.solConf.sender
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
defaultSender = fromJust $ fmap (.dst) tx <|> Set.lookupMin conf.solConf.sender <|> Just 0
let defaultSender = fromMaybe 0 $
fmap (.dst) tx <|> Set.lookupMin conf.solConf.sender
🤖 Prompt for AI Agents
In lib/Echidna/SymExec.hs at line 87, the use of fromJust on the combined Maybe
expression is unsafe and can cause a runtime exception if all options are
Nothing. Refactor the code to avoid fromJust by using a safe default value
directly with fromMaybe or by restructuring the expression to ensure a
guaranteed fallback value without partial functions.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
lib/Echidna/SymExec/Exploration.hs (1)

99-99: Duplicate comment flagged: unsafe fromJust usage.

lib/Echidna/Campaign.hs (1)

313-313: Duplicate comment flagged: unsafe fromJust usage.

🧹 Nitpick comments (5)
lib/Echidna/SymExec/Exploration.hs (4)

12-12: Remove unused import.

fromJust is imported but based on the past review comments, it seems the unsafe usage has been addressed. However, the import is still present and may not be needed.


51-51: Address HLint warning.

The pipeline shows a HLint warning about moving brackets to avoid $.

-  if (null assertSigs || selector `elem` assertSigs) && suitableForSymExec method && (unpack $ method.methodSignature) `notElem` failedProperties 
+  if (null assertSigs || selector `elem` assertSigs) && suitableForSymExec method && unpack method.methodSignature `notElem` failedProperties 

65-67: Remove commented-out code.

Dead code should be removed to maintain code cleanliness.

-      --(selector, method) = case filter (\(_, m) -> m.name == method.name) allMethods of
-      --  [] -> error $ "Method " ++ show methodName ++ " not found in contract ABI"
-      --  (x:_) -> x

68-68: Address HLint warning.

The pipeline shows a HLint warning about moving brackets to avoid $.

-      filterFunc (selector, method) = (null assertSigs || selector `elem` assertSigs) && suitableForSymExec method && (unpack $ method.methodSignature) `notElem` failedProperties
+      filterFunc (selector, method) = (null assertSigs || selector `elem` assertSigs) && suitableForSymExec method && unpack method.methodSignature `notElem` failedProperties
lib/Echidna/Campaign.hs (1)

166-166: Address HLint warning.

The pipeline shows a HLint warning about using 'when' instead of 'if...then...else'.

-    if any shrinkable tests then shrinkLoop shrinkLimit else return ()
+    when (any shrinkable tests) (shrinkLoop shrinkLimit)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69fc746 and 67820b2.

📒 Files selected for processing (4)
  • lib/Echidna/Campaign.hs (4 hunks)
  • lib/Echidna/SymExec/Exploration.hs (1 hunks)
  • lib/Echidna/Types/Test.hs (6 hunks)
  • stack.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • stack.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/Echidna/Types/Test.hs
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
lib/Echidna/SymExec/Exploration.hs (4)
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using `error` to terminate the program when encountering unexpected ABI types in `modelToTx` is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Exploration.hs:78-82
Timestamp: 2025-07-04T12:01:01.686Z
Learning: In Haskell, when working in a monad that has a MonadIO instance (like ReaderT m IO), IO actions can be used directly without explicitly calling liftIO. The compiler automatically lifts them using the MonadIO instance, making explicit liftIO calls unnecessary in such contexts.
lib/Echidna/Campaign.hs (4)
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using `error` to terminate the program when encountering unexpected ABI types in `modelToTx` is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Exploration.hs:78-82
Timestamp: 2025-07-04T12:01:01.686Z
Learning: In Haskell, when working in a monad that has a MonadIO instance (like ReaderT m IO), IO actions can be used directly without explicitly calling liftIO. The compiler automatically lifts them using the MonadIO instance, making explicit liftIO calls unnecessary in such contexts.
🪛 GitHub Actions: HLint
lib/Echidna/SymExec/Exploration.hs

[warning] 51-51: HLint suggestion: Move brackets to avoid '$' for better readability.


[warning] 68-68: HLint suggestion: Move brackets to avoid '$' for better readability.

lib/Echidna/Campaign.hs

[warning] 166-166: HLint warning: Use 'when' instead of 'if ... then ... else ...' for cleaner code.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build Echidna on macos-13
  • GitHub Check: Build Echidna on windows-latest
  • GitHub Check: Build Echidna on ubuntu-latest
  • GitHub Check: Build macOS (x86_64) binary
  • GitHub Check: Build Linux (x86_64) binary
  • GitHub Check: Build Linux (aarch64) binary
  • GitHub Check: Build macOS (aarch64) binary
🔇 Additional comments (9)
lib/Echidna/SymExec/Exploration.hs (3)

80-87: Function logic is correct.

The filterTarget function properly handles different cases for method filtering based on configuration and transaction context.


88-122: Robust threading implementation.

The exploreContract function correctly implements asynchronous symbolic execution with proper thread management using MVars for synchronization.


47-49: Error handling could be improved.

Using error for a missing method is harsh. Consider returning Nothing instead and handling it gracefully upstream.

-      (selector, method) = case filter (\(_, m) -> m.name == methodName) allMethods of
-        [] -> error $ "Method " ++ show methodName ++ " not found in contract ABI"
-        (x:_) -> x
+      methodMatch = case filter (\(_, m) -> m.name == methodName) allMethods of
+        [] -> Nothing
+        (x:_) -> Just x

Then handle the Nothing case appropriately in the surrounding logic.

⛔ Skipped due to learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using `error` to terminate the program when encountering unexpected ABI types in `modelToTx` is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
lib/Echidna/Campaign.hs (6)

125-137: Well-structured mode separation.

The conditional logic properly separates verification mode (workers=0, seqLen=1) from exploration mode, making the code intent clear.


162-180: Robust recursive loop with proper termination.

The shrinkAndRandomlyExplore function correctly implements recursive exploration with proper termination conditions and state checks.


210-232: Complex but correct transaction handling.

The txsToTxAndVmsSym function properly handles different modes (onlyRandom vs full) and correctly splits transaction sequences for symbolic execution.


241-249: Robust method selection logic.

The pattern matching on tx (Nothing vs Just) properly handles both random method selection and transaction-based method selection with appropriate error handling.


274-313: Comprehensive verification workflow.

The verifyMethods and symExecMethod functions implement a thorough verification process with proper error handling, state updates, and event reporting.


625-632: Useful helper function.

The findFailedTests function provides a clean way to filter failed tests, improving code modularity.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
lib/Echidna/Campaign.hs (1)

315-315: Unsafe fromJust – duplicate prior feedback

Using fromJust on name will crash when no contract name is provided. Handle Nothing explicitly or propagate an error.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b17a32e and cccc128.

📒 Files selected for processing (2)
  • lib/Echidna/Campaign.hs (4 hunks)
  • lib/Echidna/SymExec/Exploration.hs (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
lib/Echidna/SymExec/Exploration.hs (4)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using error to terminate the program when encountering unexpected ABI types in modelToTx is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Exploration.hs:78-82
Timestamp: 2025-07-04T12:01:01.686Z
Learning: In Haskell, when working in a monad that has a MonadIO instance (like ReaderT m IO), IO actions can be used directly without explicitly calling liftIO. The compiler automatically lifts them using the MonadIO instance, making explicit liftIO calls unnecessary in such contexts.

lib/Echidna/Campaign.hs (4)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using error to terminate the program when encountering unexpected ABI types in modelToTx is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Exploration.hs:78-82
Timestamp: 2025-07-04T12:01:01.686Z
Learning: In Haskell, when working in a monad that has a MonadIO instance (like ReaderT m IO), IO actions can be used directly without explicitly calling liftIO. The compiler automatically lifts them using the MonadIO instance, making explicit liftIO calls unnecessary in such contexts.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build macOS (x86_64) binary
  • GitHub Check: Build macOS (aarch64) binary
  • GitHub Check: Build Linux (x86_64) binary
  • GitHub Check: Build Linux (aarch64) binary
  • GitHub Check: Build Echidna on windows-latest
  • GitHub Check: Build Echidna on ubuntu-latest
  • GitHub Check: Build Echidna on macos-13
🔇 Additional comments (2)
lib/Echidna/SymExec/Exploration.hs (1)

107-108: Check constructor usage for NonInteractive

isNonInteractive = conf.uiConf.operationMode == NonInteractive Text compares a value of type OperationMode with a partial application of the constructor using Text.
Unless NonInteractive actually has the form NonInteractive Text, this will not type-check.

Please verify the intended pattern match; e.g.:

let isNonInteractive = case conf.uiConf.operationMode of
                         NonInteractive _ -> True
                         _                -> False
lib/Echidna/Campaign.hs (1)

278-281: verifyMethods passes the wrong value type to symExecMethod

contract.abiMap yields (selector, Method) pairs, but symExecMethod expects a Method.
Current code therefore fails to compile:

mapM_ (symExecMethod contract) allMethods   -- allMethods :: Map k Method

Fix by iterating over the Methods only:

-    let allMethods = contract.abiMap
-    mapM_ (symExecMethod contract) allMethods
+    let allMethods = Map.elems contract.abiMap
+    mapM_ (symExecMethod contract) allMethods
⛔ Skipped due to learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using `error` to terminate the program when encountering unexpected ABI types in `modelToTx` is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Comment on lines +265 to +266
unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Error(s) during symbolic exploration: " <> show e)) errors
unless (null partials) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Partial explored path(s) during symbolic exploration: " <> unpack e)) partials
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Mixing String and Text in error messages

show e returns String, while SymExecError expects Text. Using (<> ) on Strings then passing to a Text field will not compile under OverloadedStrings.

Convert to Text explicitly:

-    unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Error(s) during symbolic exploration: " <> show e)) errors
+    unless (null errors) $
+      mapM_ (\e -> pushWorkerEvent $ SymExecError $
+                     "Error(s) during symbolic exploration: " <> Text.pack (show e)) errors

(import qualified Data.Text as Text)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Error(s) during symbolic exploration: " <> show e)) errors
unless (null partials) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Partial explored path(s) during symbolic exploration: " <> unpack e)) partials
unless (null errors) $
mapM_ (\e -> pushWorkerEvent $ SymExecError $
"Error(s) during symbolic exploration: " <> Text.pack (show e)) errors
unless (null partials) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Partial explored path(s) during symbolic exploration: " <> unpack e)) partials
🤖 Prompt for AI Agents
In lib/Echidna/Campaign.hs around lines 265 to 266, the code mixes String and
Text types in error messages, causing compilation issues with OverloadedStrings.
Fix this by importing Data.Text qualified as Text and converting all String
values to Text explicitly using Text.pack before concatenation and passing to
SymExecError, ensuring consistent Text usage throughout.

Comment on lines +72 to +74
(Just ms, _) -> liftIO $ rElem (fromList $ map (Just . snd) $ filter (\(_, m) -> m.name `elem` ms) allMethods)
(_, []) -> return Nothing
_ -> liftIO $ rElem (fromList $ map (Just . snd) filteredMethods)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Possible runtime crash when targets list is empty

fromList throws for an empty list, so if ms is Just [] or none of the names match the ABI, the call to rElem . fromList will raise an exception before you even get to symbolic execution.

Consider guarding against an empty candidate list:

    (Just ms, _) -> do
-      liftIO $ rElem (fromList $ map (Just . snd) $ filter (\(_, m) -> m.name `elem` ms) allMethods)
+      let candidates = [snd p | p <- allMethods, m <- ms, m == (snd p).name]
+      if null candidates
+         then pure Nothing
+         else liftIO $ rElem (fromList $ map Just candidates)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(Just ms, _) -> liftIO $ rElem (fromList $ map (Just . snd) $ filter (\(_, m) -> m.name `elem` ms) allMethods)
(_, []) -> return Nothing
_ -> liftIO $ rElem (fromList $ map (Just . snd) filteredMethods)
(Just ms, _) -> do
let candidates = [snd p | p <- allMethods, m <- ms, m == (snd p).name]
if null candidates
then pure Nothing
else liftIO $ rElem (fromList $ map Just candidates)
(_, []) -> return Nothing
_ -> liftIO $ rElem (fromList $ map (Just . snd) filteredMethods)
🤖 Prompt for AI Agents
In lib/Echidna/SymExec/Exploration.hs around lines 72 to 74, the code calls
fromList on a list that can be empty when ms is Just [] or no method names
match, causing a runtime crash. To fix this, add a check to ensure the list
passed to fromList is not empty before calling rElem; if it is empty, return
Nothing instead to avoid exceptions.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
lib/Echidna/Campaign.hs (1)

315-315: Replace unsafe fromJust with explicit error handling.

Using fromJust on the contract name can cause runtime crashes. Since verification mode requires a contract name, validate this earlier or handle the Nothing case explicitly.

-        pushWorkerEvent $ SymExecLog ("Symbolic execution finished verifying contract " <> unpack (fromJust name) <> " using a single symbolic transaction."))
+        pushWorkerEvent $ SymExecLog ("Symbolic execution finished verifying contract " <> maybe "unknown" unpack name <> " using a single symbolic transaction."))
🧹 Nitpick comments (1)
tests/solidity/values/contract.sol (1)

20-23: Clarify the relationship between spawn and verification functions.

The spawn() function creates F instances but doesn't store or return them, creating a disconnect with the verification functions that expect F instances as parameters. While this might be intentional for symbolic execution testing where Echidna explores different combinations, consider adding a mechanism to track or return spawned instances for clearer test logic.

Consider this approach to improve clarity:

+  F[] public spawnedInstances;
+
   function spawn() public {
-    (new F()).init(firstRun);
+    F newF = new F();
+    newF.init(firstRun);
+    spawnedInstances.push(newF);
     firstRun = false;
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cccc128 and 79896a0.

📒 Files selected for processing (21)
  • CHANGELOG.md (1 hunks)
  • flake.nix (1 hunks)
  • lib/Echidna/ABI.hs (2 hunks)
  • lib/Echidna/Campaign.hs (7 hunks)
  • lib/Echidna/Shrink.hs (3 hunks)
  • lib/Echidna/Solidity.hs (4 hunks)
  • lib/Echidna/Transaction.hs (2 hunks)
  • lib/Echidna/Types/Campaign.hs (5 hunks)
  • lib/Echidna/Types/Solidity.hs (1 hunks)
  • lib/Echidna/UI.hs (5 hunks)
  • package.yaml (3 hunks)
  • src/test/Common.hs (6 hunks)
  • src/test/Tests/Dapptest.hs (1 hunks)
  • src/test/Tests/Integration.hs (2 hunks)
  • src/test/Tests/Values.hs (3 hunks)
  • tests/solidity/basic/gasleft.sol (1 hunks)
  • tests/solidity/basic/gasleft.yaml (1 hunks)
  • tests/solidity/basic/gasuse.yaml (1 hunks)
  • tests/solidity/basic/propGasLimit.yaml (1 hunks)
  • tests/solidity/values/contract.sol (1 hunks)
  • tests/solidity/values/contract.yaml (1 hunks)
🧠 Learnings (4)
📓 Common learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
lib/Echidna/Shrink.hs (1)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

tests/solidity/values/contract.sol (1)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

lib/Echidna/Campaign.hs (4)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using error to terminate the program when encountering unexpected ABI types in modelToTx is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Exploration.hs:78-82
Timestamp: 2025-07-04T12:01:01.686Z
Learning: In Haskell, when working in a monad that has a MonadIO instance (like ReaderT m IO), IO actions can be used directly without explicitly calling liftIO. The compiler automatically lifts them using the MonadIO instance, making explicit liftIO calls unnecessary in such contexts.

✅ Files skipped from review due to trivial changes (5)
  • tests/solidity/basic/propGasLimit.yaml
  • tests/solidity/basic/gasleft.yaml
  • lib/Echidna/ABI.hs
  • CHANGELOG.md
  • tests/solidity/values/contract.yaml
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/test/Tests/Dapptest.hs
  • package.yaml
  • lib/Echidna/Types/Solidity.hs
  • lib/Echidna/Transaction.hs
  • src/test/Tests/Values.hs
  • src/test/Tests/Integration.hs
  • lib/Echidna/UI.hs
  • src/test/Common.hs
  • lib/Echidna/Solidity.hs
  • flake.nix
  • lib/Echidna/Types/Campaign.hs
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
lib/Echidna/Shrink.hs (1)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

tests/solidity/values/contract.sol (1)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

lib/Echidna/Campaign.hs (4)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using error to terminate the program when encountering unexpected ABI types in modelToTx is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Exploration.hs:78-82
Timestamp: 2025-07-04T12:01:01.686Z
Learning: In Haskell, when working in a monad that has a MonadIO instance (like ReaderT m IO), IO actions can be used directly without explicitly calling liftIO. The compiler automatically lifts them using the MonadIO instance, making explicit liftIO calls unnecessary in such contexts.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build Linux (x86_64) binary
  • GitHub Check: Build macOS (x86_64) binary
  • GitHub Check: Build macOS (aarch64) binary
  • GitHub Check: Build Linux (aarch64) binary
  • GitHub Check: Build Echidna on windows-latest
  • GitHub Check: Build Echidna on macos-13
  • GitHub Check: Build Echidna on ubuntu-latest
🔇 Additional comments (14)
tests/solidity/basic/gasuse.yaml (1)

1-1: CI/runtime impact of raising testLimit to 10 000
Doubling the limit directly scales the number of generated test cases and can substantially lengthen symbolic-execution runs. Please confirm:

• Current CI wall-time and memory budgets still pass comfortably.
• No other YAMLs depend on testLimit defaults and now diverge unintentionally.

If needed, consider making the limit solver-/mode-dependent or exposing it via CLI to avoid blanket escalation.

tests/solidity/values/contract.sol (2)

1-14: LGTM! Clean and simple contract implementation.

Contract F provides a straightforward interface for testing with proper state management and clear function purposes.


25-37: Well-designed verification functions for symbolic execution testing.

The verification functions are thoughtfully implemented:

  • Proper use of require() for preconditions and assert() for test conditions
  • State modifications to ensure Echidna treats these as non-pure functions worth exploring
  • Clear separation of test scenarios (verify_first expects true, verify_later expects false)

This design aligns well with the reworked symbolic execution system mentioned in the PR objectives.

tests/solidity/basic/gasleft.sol (1)

1-6: LGTM! Gas range testing implementation is correct.

The contract properly uses gasleft() to test gas consumption within a specific range. The 2000-5000 gas window provides a reasonable testing boundary for gas accounting functionality.

lib/Echidna/Shrink.hs (3)

33-33: LGTM! Comment clarification improves readability.


119-119: LGTM! Comment improvement clarifies the shorten strategy.


131-131: Good improvement to shrinking flexibility.

Including the current sender (i+1 instead of i) in the uniform selection allows the sender to remain unchanged during shrinking, which can be beneficial when the current sender is already optimal. This provides better exploration of the shrinking space.

lib/Echidna/Campaign.hs (7)

21-21: LGTM! Import changes support the new symbolic execution architecture.

The added imports properly support the restructured symbolic execution functionality with appropriate modularization.

Also applies to: 24-24, 27-27, 31-31, 37-37, 43-47, 54-54, 59-60


120-136: Excellent architectural separation of verification and exploration modes.

The clear conditional logic separating symbolic verification (workers == 0 && seqLen == 1) from symbolic exploration provides a clean architectural boundary. This enables focused single-method verification vs. broader symbolic exploration workflows.


156-160: LGTM! Event handling logic is sound.

The listenerFunc properly handles NewCoverage events by triggering symbolic execution and shrinking. The hardcoded iteration count follows the previously discussed approach of using reasonable defaults.


162-180: Well-designed iterative shrinking and exploration strategy.

The recursive approach cleanly handles the interleaving of test shrinking and symbolic exploration with appropriate termination conditions based on stopOnFail and test states.


182-194: Clean helper functions for test state classification.

The predicates properly handle worker-specific shrinking and test state identification with clear, readable logic.


210-231: Complex but well-structured transaction preparation for symbolic execution.

The function correctly handles different symbolic execution strategies (targeted vs random) and properly prepares VM states and transaction sequences for each approach.


627-633: LGTM! New helper function for finding failed tests.

The findFailedTests function provides a clean way to filter failed tests, supporting the new symbolic execution logic.

Comment on lines +271 to +273
when (not newCoverage && null errors && not (null txs)) (
pushWorkerEvent $ SymExecError "No errors but symbolic execution found valid txs breaking assertions. Something is wrong.")
unless newCoverage (pushWorkerEvent $ SymExecLog "Symbolic execution finished with no new coverage.")
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify coverage validation logic for exploration mode.

The error condition assumes that finding valid transactions without new coverage is always wrong, but this may be too strict for symbolic exploration mode (as opposed to verification mode where assertion failures always create coverage).


🏁 Script executed:

#!/bin/bash
# Verify the context where this coverage validation occurs
# Check if this is in exploration or verification mode

# Search for the function context and mode indicators
rg -B 10 -A 5 "No errors but symbolic execution found valid txs breaking assertions"

Length of output: 1409


Once we find the line number, view the function body:


🏁 Script executed:

#!/bin/bash
# Locate the definition and full context of symexecTx
rg -n '^symexecTx' lib/Echidna/Campaign.hs

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Print the symexecTx function body around lines 200-330
sed -n '200,330p' lib/Echidna/Campaign.hs

Length of output: 5942


Adjust non-coverage handling in exploration mode

In exploration mode, discovering valid transactions without increasing coverage is expected. Treating this as an error is too strict. Instead of emitting a SymExecError, downgrade it to a log.

  • File: lib/Echidna/Campaign.hs
  • Around lines 271–273

Suggested patch:

   -- replace strict error on valid txs without coverage...
-  when (not newCoverage && null errors && not (null txs)) (
-    pushWorkerEvent $ SymExecError "No errors but symbolic execution found valid txs breaking assertions. Something is wrong.")
+  when (not newCoverage && null errors && not (null txs)) $
+    pushWorkerEvent $ SymExecLog "Symbolic execution found valid transactions without new coverage."

   -- …then always log if there’s no new coverage
   unless newCoverage (pushWorkerEvent $ SymExecLog "Symbolic execution finished with no new coverage.")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
when (not newCoverage && null errors && not (null txs)) (
pushWorkerEvent $ SymExecError "No errors but symbolic execution found valid txs breaking assertions. Something is wrong.")
unless newCoverage (pushWorkerEvent $ SymExecLog "Symbolic execution finished with no new coverage.")
when (not newCoverage && null errors && not (null txs)) $
pushWorkerEvent $ SymExecLog "Symbolic execution found valid transactions without new coverage."
unless newCoverage (pushWorkerEvent $ SymExecLog "Symbolic execution finished with no new coverage.")
🤖 Prompt for AI Agents
In lib/Echidna/Campaign.hs around lines 271 to 273, the code currently treats
finding valid transactions without new coverage as an error by emitting
SymExecError. Change this behavior to emit a SymExecLog instead, downgrading the
severity from error to log to reflect that this is expected in exploration mode.
Replace the pushWorkerEvent call with SymExecLog for this case.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
CHANGELOG.md (2)

5-5: Add release date for 2.2.7 header

The previous sections carry dates; inserting one keeps the changelog machine-parsable and consistent.


11-11: Validate ARM64 Linux build artefacts ship via CI

Cross-arch builds often miss CI packaging/signing steps the first time around. Sanity-check that nightly/release workflows now publish functional ARM64 binaries.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79896a0 and a267e84.

📒 Files selected for processing (16)
  • CHANGELOG.md (1 hunks)
  • README.md (3 hunks)
  • lib/Echidna.hs (2 hunks)
  • lib/Echidna/Campaign.hs (8 hunks)
  • lib/Echidna/Config.hs (2 hunks)
  • lib/Echidna/Exec.hs (5 hunks)
  • lib/Echidna/Output/JSON.hs (2 hunks)
  • lib/Echidna/Solidity.hs (4 hunks)
  • lib/Echidna/Types/Campaign.hs (5 hunks)
  • lib/Echidna/UI.hs (7 hunks)
  • lib/Echidna/UI/Report.hs (5 hunks)
  • src/test/Common.hs (5 hunks)
  • src/test/Tests/Seed.hs (1 hunks)
  • tests/solidity/basic/default.yaml (1 hunks)
  • tests/solidity/basic/gasuse.sol (0 hunks)
  • tests/solidity/basic/gasuse.yaml (0 hunks)
🧠 Learnings (3)
📓 Common learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
lib/Echidna/Campaign.hs (4)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using error to terminate the program when encountering unexpected ABI types in modelToTx is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Exploration.hs:78-82
Timestamp: 2025-07-04T12:01:01.686Z
Learning: In Haskell, when working in a monad that has a MonadIO instance (like ReaderT m IO), IO actions can be used directly without explicitly calling liftIO. The compiler automatically lifts them using the MonadIO instance, making explicit liftIO calls unnecessary in such contexts.

lib/Echidna/Types/Campaign.hs (3)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using error to terminate the program when encountering unexpected ABI types in modelToTx is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

🪛 GitHub Actions: CI
lib/Echidna/Types/Campaign.hs

[error] 13-13: GHC Warning treated as error: The import of ‘Echidna.Types.Tx’ is redundant except perhaps to import instances. Use 'import Echidna.Types.Tx()' to import instances alone. (unused-imports)

💤 Files with no reviewable changes (2)
  • tests/solidity/basic/gasuse.yaml
  • tests/solidity/basic/gasuse.sol
✅ Files skipped from review due to trivial changes (2)
  • README.md
  • lib/Echidna/UI/Report.hs
🚧 Files skipped from review as they are similar to previous changes (9)
  • tests/solidity/basic/default.yaml
  • lib/Echidna/UI.hs
  • lib/Echidna.hs
  • lib/Echidna/Exec.hs
  • lib/Echidna/Output/JSON.hs
  • lib/Echidna/Solidity.hs
  • src/test/Tests/Seed.hs
  • src/test/Common.hs
  • lib/Echidna/Config.hs
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
lib/Echidna/Campaign.hs (4)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using error to terminate the program when encountering unexpected ABI types in modelToTx is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Exploration.hs:78-82
Timestamp: 2025-07-04T12:01:01.686Z
Learning: In Haskell, when working in a monad that has a MonadIO instance (like ReaderT m IO), IO actions can be used directly without explicitly calling liftIO. The compiler automatically lifts them using the MonadIO instance, making explicit liftIO calls unnecessary in such contexts.

lib/Echidna/Types/Campaign.hs (3)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using error to terminate the program when encountering unexpected ABI types in modelToTx is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

🪛 GitHub Actions: CI
lib/Echidna/Types/Campaign.hs

[error] 13-13: GHC Warning treated as error: The import of ‘Echidna.Types.Tx’ is redundant except perhaps to import instances. Use 'import Echidna.Types.Tx()' to import instances alone. (unused-imports)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Linux (x86_64) binary
  • GitHub Check: Build Linux (aarch64) binary
  • GitHub Check: Build macOS (x86_64) binary
  • GitHub Check: Build macOS (aarch64) binary
🔇 Additional comments (22)
CHANGELOG.md (4)

2-3: Ensure ancillary docs & CLI flags reflect dropped Etheno / estimateGas support

These bullets announce permanent removals. Double-check that:

  1. All user-facing docs/examples have been purged or updated.
  2. CLI options or config keys that toggled these features are now rejected with a clear error.

Missed stragglers confuse users and break scripts.


8-9: Confirm new UI navigation & gas/s metrics are covered by tests

UI-heavy changes are prone to regress. Ensure automated UI or integration tests exercise:

  • Tab / PgUp / PgDown / arrow flows
  • Correct gas-per-second rendering in headless mode
    Otherwise future refactors may silently break them.

13-15: Regression tests needed for shrinking & gas-accounting fixes

Given past fragility in these areas, add unit/integration tests asserting:

  • Shrink steps monotonically decrease size.
  • Intrinsic gas is always included in total gas calculation.

Locks the bug closed.


16-19: Pin CI matrix to upgraded hevm & GHC 9.8.4

Toolchain bumps can break build scripts and cached artefacts. Make sure:

  • Docker images and nix flakes reference the new versions.
  • CI caches are invalidated, otherwise subtle ABI mismatches may surface only in prod.
lib/Echidna/Types/Campaign.hs (3)

45-47: Good addition of SMT solver configuration.

The new symExecSMTSolver field properly supports the enhanced symbolic execution functionality with clear documentation of supported solvers.


48-66: Appropriate configuration updates for symbolic execution refactoring.

The comment updates correctly remove references to concolic execution, and the new symExecMaxExplore field provides needed control over exploration depth. The reduced default for symExecMaxIters (10→5) is reasonable for the new implementation.


114-118: Well-defined defaults for new symbolic execution parameters.

The new defaultSymExecMaxExplore constant and the updated defaultSymExecMaxIters value provide sensible defaults for the enhanced symbolic execution functionality.

lib/Echidna/Campaign.hs (15)

21-60: Import changes support the symbolic execution refactoring.

The new imports properly reflect the modular approach to symbolic execution with separate modules for common utilities, exploration, and verification. The imports align with the architectural changes described in the PR objectives.


119-136: Well-implemented dual-mode symbolic execution.

The conditional logic properly distinguishes between verification mode (workers=0, seqLen=1) and exploration mode, implementing the architectural changes described in the PR objectives. The mode selection is clear and appropriate.


154-158: Listener function properly handles new coverage events.

The function correctly processes coverage events by performing symbolic execution and exploration. This aligns with the new symbolic exploration workflow described in the PR objectives.


160-178: Effective implementation of exploration strategy.

The shrinkAndRandomlyExplore function properly implements the iterative approach combining shrinking and symbolic exploration. The recursive structure with countdown and conditional logic for stopping on failures is well-designed.


180-203: Well-structured helper functions for test management.

The helper functions (shrinkable, final, shrinkLoop) provide clear abstractions for test state management and worker-specific processing. The logic is sound and supports the distributed fuzzing approach.


208-230: Complex but necessary transaction preparation logic.

The txsToTxAndVmsSym function implements the two strategies for symbolic transaction introduction mentioned in the PR objectives: random insertion and last transaction symbolization. While complex, the logic properly handles the different modes and VM state preparation.


232-249: Appropriate target method selection and exploration dispatch.

The symexecTx function properly implements the target method selection logic, handling both random method selection and transaction-specific targeting. The pattern matching approach clearly separates the two exploration strategies.


250-272: Proper implementation of exploration workflow.

The exploreAndVerify function correctly handles the symbolic exploration workflow with appropriate thread management, result processing, and error handling. The coverage validation logic is appropriate for exploration mode.


273-278: Clean implementation of verification mode.

The verifyMethods function properly implements the verification mode by systematically verifying all contract methods. This aligns with the formal verification approach described in the PR objectives.


280-314: Comprehensive method verification implementation.

The symExecMethod function properly handles individual method verification with appropriate error handling and test state updates. The logic for handling assertion failures and the "unsolvable" state aligns with the verification mode objectives.

Based on previous discussions, the coverage validation logic is correct for verification mode where assertion failures inherently create new coverage.


494-514: Appropriate refactoring of callseq function.

The changes to the callseq function appear to be minor refactoring that maintains the core transaction execution and coverage tracking functionality while improving code organization.


538-542: Good encapsulation of corpus management logic.

Making addToCorpus a local function improves encapsulation and maintains the same logic for managing successful transactions in the corpus.


549-559: Maintained functionality in execTxOptC.

The changes to execTxOptC maintain the essential transaction execution with coverage optimization functionality while adapting to the new architecture.


583-584: Minor structural improvements to evalSeq.

The changes maintain the core sequential transaction execution logic while making minor structural improvements to the recursive processing.


600-607: Useful utility function for test filtering.

The findFailedTests function provides a clean abstraction for finding failed tests, supporting the symbolic execution targeting logic described in the PR objectives.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
lib/Echidna/SymExec/Common.hs (1)

48-52: Logical error in suitableForSymExec due to operator precedence.

This issue has already been identified and tracked in issue crytic#1390. The current implementation incorrectly allows methods with dynamic inputs or blacklisted methods due to operator precedence.

🧹 Nitpick comments (1)
lib/Echidna/SymExec/Common.hs (1)

228-229: Remove commented-out debug code.

Clean up the commented debug statements for better code maintainability.

-  --liftIO $ putStrLn ("Exploring: " ++ T.unpack method.methodSignature)
-  --pushWorkerEvent undefined
   calldataSym@(cd, constraints) <- mkCalldata (Just (Sig method.methodSignature (snd <$> method.inputs))) []

...

   let results = map fst models
-  --liftIO $ mapM_ TIO.putStrLn partials
   return (map (modelToTx dst vm.block.timestamp vm.block.number method conf.solConf.sender defaultSender) results, map formatPartial partials)

Also applies to: 248-248

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2806951 and eb66868.

📒 Files selected for processing (2)
  • lib/Echidna/SymExec/Common.hs (1 hunks)
  • stack.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • stack.yaml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using `error` to terminate the program when encountering unexpected ABI types in `modelToTx` is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.
lib/Echidna/SymExec/Common.hs (3)

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using error to terminate the program when encountering unexpected ABI types in modelToTx is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using take 1 shuffleMethods to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Learnt from: gustavo-grieco
PR: #10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build macOS (x86_64) binary
  • GitHub Check: Build macOS (aarch64) binary
  • GitHub Check: Build Echidna on macos-13
  • GitHub Check: Build Linux (aarch64) binary
  • GitHub Check: Build Echidna on windows-latest
  • GitHub Check: Build Echidna on ubuntu-latest
  • GitHub Check: Build Linux (x86_64) binary

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
lib/Echidna/Campaign.hs (2)

267-269: Reconsider error condition for exploration mode.

The logic treats finding valid transactions without new coverage as an error, but in exploration mode (unlike verification mode), this can be a legitimate outcome. Valid transactions may not always increase coverage during exploration.


261-262: Fix String/Text type mismatch in error messages.

The code mixes String (from show e) with Text literals, causing compilation issues with OverloadedStrings. The <> operator and SymExecError expect consistent Text types.

Apply this fix:

+import Data.Text qualified as Text
+
-unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Error(s) during symbolic exploration: " <> show e)) errors
+unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Error(s) during symbolic exploration: " <> Text.pack (show e))) errors

-unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Error(s) solving constraints produced by method " <> methodSignature <> ": " <> show e)) errors
+unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Error(s) solving constraints produced by method " <> methodSignature <> ": " <> Text.pack (show e))) errors

Also applies to: 293-294

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 85c7247 and 01dcc8e.

📒 Files selected for processing (2)
  • lib/Echidna/Campaign.hs (4 hunks)
  • lib/Echidna/Server.hs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/Echidna/Server.hs
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using `error` to terminate the program when encountering unexpected ABI types in `modelToTx` is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.
📚 Learning: in echidna's symbolic execution verification mode (lib/echidna/campaign.hs verifymethod function), s...
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/Campaign.hs:259-261
Timestamp: 2025-07-04T12:03:36.010Z
Learning: In Echidna's symbolic execution verification mode (lib/Echidna/Campaign.hs verifyMethod function), symbolic execution specifically targets assertion failures. When assertion failures are triggered, they inherently create new coverage by definition. Therefore, finding valid transactions without new coverage in verification mode indicates a logic error, unlike in exploration mode where valid transactions may legitimately exist without increasing coverage.

Applied to files:

  • lib/Echidna/Campaign.hs
📚 Learning: in echidna's symbolic execution (lib/echidna/symexec.hs), the exploration is intentionally limited t...
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec.hs:102-102
Timestamp: 2025-06-03T11:55:54.240Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec.hs), the exploration is intentionally limited to one method at a time using `take 1 shuffleMethods` to improve user experience by providing faster symbolic execution results rather than waiting for all methods to be explored.

Applied to files:

  • lib/Echidna/Campaign.hs
📚 Learning: in echidna's symbolic execution (lib/echidna/symexec/common.hs), using `error` to terminate the prog...
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Common.hs:139-139
Timestamp: 2025-07-07T12:24:07.157Z
Learning: In Echidna's symbolic execution (lib/Echidna/SymExec/Common.hs), using `error` to terminate the program when encountering unexpected ABI types in `modelToTx` is acceptable and preferred over graceful error handling, as it indicates a programming error that should cause immediate termination.

Applied to files:

  • lib/Echidna/Campaign.hs
📚 Learning: in haskell, when working in a monad that has a monadio instance (like readert m io), io actions can ...
Learnt from: gustavo-grieco
PR: gustavo-grieco/echidna#10
File: lib/Echidna/SymExec/Exploration.hs:78-82
Timestamp: 2025-07-04T12:01:01.686Z
Learning: In Haskell, when working in a monad that has a MonadIO instance (like ReaderT m IO), IO actions can be used directly without explicitly calling liftIO. The compiler automatically lifts them using the MonadIO instance, making explicit liftIO calls unnecessary in such contexts.

Applied to files:

  • lib/Echidna/Campaign.hs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build macOS (x86_64) binary
🔇 Additional comments (7)
lib/Echidna/Campaign.hs (7)

21-21: Import updates align with symbolic execution restructuring.

The import changes correctly reflect the modular redesign of symbolic execution, adding necessary dependencies for the new verification and exploration modes.

Also applies to: 24-24, 27-27, 35-35, 42-46, 53-53, 58-59


119-133: Well-structured dual-mode symbolic execution.

The conditional logic correctly implements the two symbolic execution modes described in the PR objectives:

  • Verification mode: Triggered when workers == Just 0 and seqLen == 1
  • Exploration mode: Uses event listener loop for fuzzing integration

The implementation aligns well with the stated goal of supporting both stateless verification and exploration modes.


152-201: Solid iterative shrinking and exploration logic.

The shrinkAndRandomlyExplore function correctly implements:

  • Worker-specific shrinking to avoid contention (using test.workerId == Just workerId)
  • Proper termination conditions with countdown parameter
  • Logical flow: shrink first, then explore, with early exit on failures

The recursive design with bounded iterations ensures the process will terminate while maximizing shrinking opportunities.


204-228: Comprehensive transaction preparation for symbolic execution.

The txsToTxAndVmsSym function effectively implements both symbolic transaction strategies mentioned in the PR objectives:

  1. Normal mode: Prepares both last transaction symbolization and random position insertion
  2. Random-only mode: Focuses on random position insertion

The VM state building using foldlM with execTx correctly constructs intermediate states for symbolic exploration.


230-247: Well-structured symbolic execution orchestration.

The symexecTx function correctly implements the symbolic execution workflow:

  • Uses chooseContract for contract selection
  • Handles both random method targeting and transaction-specific targeting
  • Proper integration with failed test signatures for assertion-focused exploration

600-607: Clean utility function for test filtering.

The findFailedTests function provides a straightforward way to identify failed tests for symbolic execution targeting. The implementation correctly reads test references and applies the didFail filter.


154-155: Proper integration of symbolic execution with existing workflow.

The integration points correctly connect symbolic execution with the existing fuzzing and shrinking workflow:

  • symexecTxs False enables both symbolic strategies (not random-only)
  • Integration with shrinkAndRandomlyExplore maintains the iterative improvement cycle

Also applies to: 202-202

Comment on lines +271 to +311
verifyMethods = do
dapp <- asks (.dapp)
let cs = Map.elems dapp.solcByName
contract <- chooseContract cs name
let allMethods = contract.abiMap
mapM_ (symExecMethod contract) allMethods

symExecMethod contract method = do
lift callback
(threadId, symTxsChan) <- verifyMethod method contract vm

modify' (\ws -> ws { runningThreads = [threadId] })
lift callback

(symTxs, partials) <- liftIO $ takeMVar symTxsChan
let txs = extractTxs symTxs
let errors = extractErrors symTxs

modify' (\ws -> ws { runningThreads = [] })
lift callback
let methodSignature = unpack method.methodSignature
if not (null partials) || not (null errors) then do
unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Error(s) solving constraints produced by method " <> methodSignature <> ": " <> show e)) errors
unless (null partials) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Partial explored path(s) during symbolic verification of method " <> methodSignature <> ": " <> unpack e)) partials
updateTests $ \test -> do
if isOpen test && isAssertionTest test && getAssertionSignature test == methodSignature then
pure $ Just $ test { Test.state = Passed }
else
pure $ Just test
else do
-- We can't do callseq vm' [symTx] because callseq might post the full call sequence as an event
newCoverage <- or <$> mapM (\symTx -> snd <$> callseq vm [symTx]) txs

unless newCoverage ( do
unless (null txs) $ error "No new coverage but symbolic execution found valid txs. Something is wrong."
updateTests $ \test -> do
if isOpen test && isAssertionTest test && getAssertionSignature test == methodSignature then
pure $ Just $ test { Test.state = Unsolvable }
else
pure $ Just test
pushWorkerEvent $ SymExecLog ("Symbolic execution finished verifying contract " <> unpack (fromJust name) <> " using a single symbolic transaction."))
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Solid verification mode implementation with proper state management.

The verifyMethods function correctly implements symbolic verification:

  • Iterates through all contract methods for comprehensive verification
  • Updates test states appropriately (Passed for errors/partials, Unsolvable for no coverage)
  • The coverage validation logic at Line 305 is correct for verification mode, as assertion failures inherently create new coverage in this context (per retrieved learnings)

However, Line 311 uses fromJust name which could crash if the contract name is Nothing.


🏁 Script executed:

#!/bin/bash
# Verify that contract name is guaranteed to be present in verification mode
ast-grep --pattern 'runSymWorker $_ $_ $_ $_ $_ name' -A 10 -B 5

Length of output: 1250


🏁 Script executed:

#!/bin/bash
# Inspect runSymWorker to see how verifyMethods is invoked and how 'name' is used
rg -n "runSymWorker" -A 50 -B 10 lib/Echidna/Campaign.hs

# Show the definition of chooseContract to determine how it handles a Nothing name
rg -n "chooseContract" -A 10 -B 10 lib/Echidna/Campaign.hs

Length of output: 6355


🏁 Script executed:

#!/bin/bash
# Locate the file defining chooseContract
echo "Searching for file containing 'chooseContract' definition..."
FILE=$(rg -l "chooseContract :: " -n . | head -n1)
echo "Found in: $FILE"

# Display the implementation of chooseContract
echo "Showing 'chooseContract' implementation:"
rg -n "chooseContract :: " -A 20 -B 0 "$FILE"

Length of output: 1508


Fix unsafe use of fromJust in verification logging

The log at lib/Echidna/Campaign.hs:311 uses fromJust name, which will crash if the user did not supply a contract name (name == Nothing). Since chooseContract cs name already defaults to the first contract when name is Nothing, use the selected contract.contractName instead:

  • File: lib/Echidna/Campaign.hs
    Line ~311

Suggested change:

-    pushWorkerEvent $ SymExecLog
-      ("Symbolic execution finished verifying contract " <> unpack (fromJust name)
-        <> " using a single symbolic transaction.")
+    let loggedName = unpack contract.contractName
+    pushWorkerEvent $ SymExecLog
+      ("Symbolic execution finished verifying contract " <> loggedName
+        <> " using a single symbolic transaction.")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
verifyMethods = do
dapp <- asks (.dapp)
let cs = Map.elems dapp.solcByName
contract <- chooseContract cs name
let allMethods = contract.abiMap
mapM_ (symExecMethod contract) allMethods
symExecMethod contract method = do
lift callback
(threadId, symTxsChan) <- verifyMethod method contract vm
modify' (\ws -> ws { runningThreads = [threadId] })
lift callback
(symTxs, partials) <- liftIO $ takeMVar symTxsChan
let txs = extractTxs symTxs
let errors = extractErrors symTxs
modify' (\ws -> ws { runningThreads = [] })
lift callback
let methodSignature = unpack method.methodSignature
if not (null partials) || not (null errors) then do
unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Error(s) solving constraints produced by method " <> methodSignature <> ": " <> show e)) errors
unless (null partials) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e -> "Partial explored path(s) during symbolic verification of method " <> methodSignature <> ": " <> unpack e)) partials
updateTests $ \test -> do
if isOpen test && isAssertionTest test && getAssertionSignature test == methodSignature then
pure $ Just $ test { Test.state = Passed }
else
pure $ Just test
else do
-- We can't do callseq vm' [symTx] because callseq might post the full call sequence as an event
newCoverage <- or <$> mapM (\symTx -> snd <$> callseq vm [symTx]) txs
unless newCoverage ( do
unless (null txs) $ error "No new coverage but symbolic execution found valid txs. Something is wrong."
updateTests $ \test -> do
if isOpen test && isAssertionTest test && getAssertionSignature test == methodSignature then
pure $ Just $ test { Test.state = Unsolvable }
else
pure $ Just test
pushWorkerEvent $ SymExecLog ("Symbolic execution finished verifying contract " <> unpack (fromJust name) <> " using a single symbolic transaction."))
symExecMethod contract method = do
lift callback
(threadId, symTxsChan) <- verifyMethod method contract vm
modify' (\ws -> ws { runningThreads = [threadId] })
lift callback
(symTxs, partials) <- liftIO $ takeMVar symTxsChan
let txs = extractTxs symTxs
let errors = extractErrors symTxs
modify' (\ws -> ws { runningThreads = [] })
lift callback
let methodSignature = unpack method.methodSignature
if not (null partials) || not (null errors) then do
unless (null errors) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e ->
"Error(s) solving constraints produced by method "
<> methodSignature <> ": " <> show e)) errors
unless (null partials) $ mapM_ ((pushWorkerEvent . SymExecError) . (\e ->
"Partial explored path(s) during symbolic verification of method "
<> methodSignature <> ": " <> unpack e)) partials
updateTests $ \test -> do
if isOpen test
&& isAssertionTest test
&& getAssertionSignature test == methodSignature
then pure $ Just $ test { Test.state = Passed }
else pure $ Just test
else do
-- We can't do callseq vm' [symTx] because callseq might post the full call sequence as an event
newCoverage <- or <$> mapM (\symTx -> snd <$> callseq vm [symTx]) txs
unless newCoverage ( do
unless (null txs) $
error "No new coverage but symbolic execution found valid txs. Something is wrong."
updateTests $ \test -> do
if isOpen test
&& isAssertionTest test
&& getAssertionSignature test == methodSignature
then pure $ Just $ test { Test.state = Unsolvable }
else pure $ Just test
let loggedName = unpack contract.contractName
pushWorkerEvent $ SymExecLog
("Symbolic execution finished verifying contract "
<> loggedName
<> " using a single symbolic transaction.")
)
🤖 Prompt for AI Agents
In lib/Echidna/Campaign.hs around lines 271 to 311, the code uses fromJust name
in the final logging statement, which can cause a runtime crash if name is
Nothing. Since chooseContract defaults to a contract when name is Nothing,
replace fromJust name with contract.contractName to safely reference the
selected contract's name in the log message.

@gustavo-grieco gustavo-grieco deleted the fixed-sym-exec branch September 14, 2025 17:26
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.

2 participants