Skip to content

Store event changes into Echidna's corpus#1405

Merged
elopez merged 11 commits intocrytic:masterfrom
divyaranjan1905:master
Aug 3, 2025
Merged

Store event changes into Echidna's corpus#1405
elopez merged 11 commits intocrytic:masterfrom
divyaranjan1905:master

Conversation

@divyaranjan1905
Copy link
Copy Markdown
Contributor

@divyaranjan1905 divyaranjan1905 commented Jul 24, 2025

Fixes #883. Only implements the storing of events, not of the entire state's variables etc.

Regards,

Divya.

-- Now we try to parse the return values as solidity constants, and add them to 'GenDict'
resultMap = returnValues results workerState.genDict.rTypes
-- compute the new events to be stored
eventDiffs = extractEventValues vm.dappInfo results
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure if this is how it should be done with regards to the dappInfo, since there's vm.dapp and vm.dappInfo. Where can I find more information about them?

@gustavo-grieco
Copy link
Copy Markdown
Collaborator

Please provide a small solidity code that shows an assertion failure that needs this new feature. We will show you how to add a test

@divyaranjan1905
Copy link
Copy Markdown
Contributor Author

Please provide a small solidity code that shows an assertion failure that needs this new feature. We will show you how to add a test

Something like this?

contract EventAssert {
    event ValueSet(uint256 indexed value);

    function setValue(uint256 value) public {
        emit ValueSet(value);
        assert(value != 1295);
    }
}

@gustavo-grieco
Copy link
Copy Markdown
Collaborator

Your example needs to have an event emmited with a value that can be used to trigger the assertion, but it needs to be essential for that. Usually you need to do some computation and emit some value from it, that is normally hidden from the fuzzer.

@divyaranjan1905
Copy link
Copy Markdown
Contributor Author

divyaranjan1905 commented Jul 25, 2025

How about this @gustavo-grieco?

contract EventAssert {
    uint private secret = 123;
    event Output(uint result);

    function check(uint x) public {
        uint result = x % 200;
        emit Output(result);
        if (result == secret) {
            assert(false);
        }
    }
}

@gustavo-grieco
Copy link
Copy Markdown
Collaborator

Uhm it looks bette but I'm not sure the value from the event is useful to break the assertion. Try using a hashed value instead 🤔

@divyaranjan1905
Copy link
Copy Markdown
Contributor Author

So, something like this:

contract EventAssert {
    uint private secret;

    event Computed(uint result);

    constructor() {
        secret = uint(keccak256(abi.encodePacked(block.timestamp))) % 1000;
    }

    function check(uint x) public {
        uint result = (x * 3 + 7) % 1000;
        emit Computed(result);
        if (result == secret) {
            assert(false);
        }
    }
}

@gustavo-grieco
Copy link
Copy Markdown
Collaborator

So if you remove emit Computed(result), echidna will not able to find the value to break it, right? (I haven't tested it)

@divyaranjan1905
Copy link
Copy Markdown
Contributor Author

divyaranjan1905 commented Jul 26, 2025

So if you remove emit Computed(result), echidna will not able to find the value to break it, right? (I haven't tested it)

I think so, because if we don't emit, Echidna has no way of knowing about it.

Let me know how to go further with the testing.

@divyaranjan1905
Copy link
Copy Markdown
Contributor Author

Changed the contract following @elopez's suggestion:

contract EventAssert {
    uint private secret;
    event Secret(uint result);
    function reset() public {
        secret = uint(keccak256(abi.encodePacked(block.timestamp)));
        emit Secret(secret);
    }
    function check(uint x) public {
        if (x == secret) {
            assert(false);
        }
    }
}

@divyaranjan1905
Copy link
Copy Markdown
Contributor Author

This is the result:

image

On the left is upstream Echidna without the changes, and on the right is my fork:

@gustavo-grieco
Copy link
Copy Markdown
Collaborator

This looks good, let's add it as a test in the "value" section

@divyaranjan1905
Copy link
Copy Markdown
Contributor Author

The fork doesn't reproduce the revert deterministically though, but I guess that's expected because of the hash being quite a huge one.

@divyaranjan1905 divyaranjan1905 marked this pull request as ready for review July 27, 2025 06:07
@divyaranjan1905 divyaranjan1905 requested a review from arcz as a code owner July 27, 2025 06:07
@gustavo-grieco
Copy link
Copy Markdown
Collaborator

You mean that this test is not reliable ?

Copy link
Copy Markdown
Collaborator

@gustavo-grieco gustavo-grieco left a comment

Choose a reason for hiding this comment

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

This needs to be executed as a test. Check the values.hs file

extractEventValues :: DappInfo -> VM Concrete s -> VM Concrete s -> Map AbiType (Set AbiValue)
extractEventValues dappInfo vm vm' =
let
oldLogs = vm.logs
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think logs are cleaned from one van to the next one 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Isn't that how it should be?

@divyaranjan1905
Copy link
Copy Markdown
Contributor Author

You mean that this test is not reliable ?

I wasn't sure if that's expected ir not. It only gives the output I sent a few times and at other times it doesn't revert and says everything passes.

* descending order is more efficient
* Map.unionsWith can elegantly replace foldl
Copy link
Copy Markdown
Member

@elopez elopez left a comment

Choose a reason for hiding this comment

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

Thanks for working on this!

@elopez elopez merged commit 5e99537 into crytic:master Aug 3, 2025
19 checks passed
datradito pushed a commit to datradito/echidna-mcp that referenced this pull request Dec 29, 2025
* modify callseq to store event changes into the corpus

* fixes crytic#883

* chore: fix imports

* fix: simplify extractEventValues to take two vms and return the emmitted logs between them

* chore: remove redundant imports

* add test

* add campaign test to valueTests

* modify campaign test to take a seed value

* add campaign.yaml

* rename test from campaign to events

* reduce test limit

* fix ordering in callseq for computing additions and constants

* descending order is more efficient
* Map.unionsWith can elegantly replace foldl
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.

Feature Request: Add values from state changes and/or events to corpus

3 participants