-
Notifications
You must be signed in to change notification settings - Fork 47
Niv/cert 8248 revert example #155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| /** | ||
| * @title C | ||
| * @dev A simple contract demonstrating functions that can revert | ||
| */ | ||
| contract C { | ||
| bool public fooWasCalled; | ||
|
|
||
| /** | ||
| * @notice Sets fooWasCalled and reverts if the input b is false | ||
| */ | ||
| function foo(bool b) external { | ||
| fooWasCalled = true; | ||
| if (!b) { | ||
| revert("foo reverted"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @notice Calls foo and can therefore revert if foo reverts | ||
| */ | ||
| function callFooFromContract(bool b) external { | ||
| this.foo(b); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Dummy function that can be used to demonstrate "summary" usage | ||
| */ | ||
| function summarizeMe(bool b) external {} | ||
|
|
||
| /** | ||
| * @notice Returns b or reverts if b is false | ||
| */ | ||
| function canRevert(bool b) external returns (bool) { | ||
| if (!b) { | ||
| revert("canRevert reverted"); | ||
| } | ||
| return b; | ||
| } | ||
|
|
||
| /** | ||
| * @notice Calls summarizeMe and can be used for demonstration of deeper calls | ||
| */ | ||
| function callSummarizeMe(bool b) external { | ||
| this.summarizeMe(b); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # CVL Revert Feature Example | ||
|
|
||
| This folder demonstrates the newly introduced **CVL revert functionality** and the **`@withrevert`** annotation. These features allow you to: | ||
|
|
||
| 1. **Explicitly revert** inside CVL functions (e.g. `revert("some reason")`). | ||
| 2. **Capture revert signals** when calling any function—Solidity or CVL—using the `@withrevert` annotation, which sets the built-in CVL variable `lastReverted` to `true` if a revert occurs. | ||
|
|
||
| ## Files | ||
|
|
||
| 1. **`C.sol`** | ||
| A simple Solidity contract with functions that may revert. The contract contains: | ||
| - `foo(bool b)`: sets a flag and reverts if `b` is false. | ||
| - `canRevert(bool b)`: returns `b` or reverts if `b` is false. | ||
| - `summarizeMe(bool b)`: a dummy function, used for demonstrating a CVL summary that can also revert. | ||
| - `callSummarizeMe(bool b)`: calls `summarizeMe`; used to show how reverts in a summary can bubble up. | ||
| - Additional helper functions like `callFooFromContract`. | ||
|
|
||
| 2. **`example.spec`** | ||
| A CVL spec demonstrating: | ||
| - Calling a Solidity function with `@withrevert`, e.g. `foo@withrevert(b)`. | ||
| - Writing a CVL function (`cvlFunctionThatMayRevert`) that explicitly reverts when a condition fails. | ||
| - Two different ways to handle potential reverts from `canRevert(bool)`, illustrating how revert can either be captured inline or bubble up through a CVL wrapper. | ||
| - A **“summarize”** function, `cvlSummarizeMe`, that can revert if its boolean parameter is false, invoked via the contract function `callSummarizeMe@withrevert(b)`. | ||
|
|
||
| ## Usage | ||
|
|
||
| 1. **Command** | ||
| ```shell | ||
| certoraRun example.conf --server production --prover_version master | ||
| ``` | ||
| 2. **Expected Output** | ||
| - The output should confirm that the spec passes, with the expected behavior of `lastReverted` in each case. | ||
| - Link: [Example Output](https://prover.certora.com/output/1512/3919ff99f4e84203a04e4aeab4fc8c11?anonymousKey=3a7682aefc6e9b26f248f7fc7a1aff60557281cf) | ||
|
|
||
| 3. **Examine the Rules** | ||
| - **`testFooWithRevert`** | ||
| Confirms that calling `foo` with `@withrevert` sets `lastReverted` exactly when the argument is false. | ||
| - **`testCvlFunctionWithRevert`** | ||
| Demonstrates how a CVL function (`cvlFunctionThatMayRevert`) can explicitly revert, and how `@withrevert` captures that. | ||
| - **`testCanRevertInline`** | ||
| Uses `wrapperForCanRevertInline(bool)`, which internally calls `canRevert@withrevert(condition)`, returning `lastReverted`. | ||
| - **`testCanRevertBubbleUp`** | ||
| Illustrates revert **bubbling up**. In `wrapperForCanRevertBubble(bool)`, we call `canRevert(condition)` *without* `@withrevert`, but then call the wrapper itself with `@withrevert`. The internal revert propagates, setting `lastReverted`. | ||
| - **`testCallSummarizeMe`** | ||
| Demonstrates a contract function (`callSummarizeMe`) that calls a **CVL summary** (`cvlSummarizeMe`). If the summary reverts (when the boolean input is false), the revert bubbles up to the contract caller, setting `lastReverted`. | ||
|
|
||
| ## Conclusion | ||
|
|
||
| With these files (`C.sol`, `example.spec`, `Example.conf`, and this `README.md`), you can see how the new CVL revert feature works in practice. By enabling the `-cvlFunctionRevert` flag, you can write more expressive and accurate specs that treat reverts in CVL and Solidity calls consistently, capturing those reverts in **`lastReverted`** for precise modeling in your verification. The **bubble-up** behavior especially highlights how deeper CVL calls (including **summaries**) can now propagate reverts just like Solidity calls. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "files": [ | ||
| "C.sol" | ||
| ], | ||
| "independent_satisfy": true, | ||
| "process": "emv", | ||
| "solc": "solc8.26", | ||
| "verify": "C:example.spec", | ||
| "prover_args": [ | ||
| "-cvlFunctionRevert true" | ||
| ], | ||
| "rule_sanity": "basic" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| // --------------------------------------------------------- | ||
| // 1. Declare the contract methods we want to verify/inspect | ||
| // --------------------------------------------------------- | ||
| methods { | ||
| // Contract functions that may revert | ||
| function foo(bool) external envfree; | ||
| function canRevert(bool) external returns bool envfree; | ||
|
|
||
| // For demonstrating summary usage | ||
| function callSummarizeMe(bool) external envfree; | ||
| function summarizeMe(bool b) external => cvlSummarizeMe(b); | ||
| } | ||
|
|
||
| // --------------------------------------------------------- | ||
| // 2. CVL function that explicitly uses the 'revert' keyword | ||
| // --------------------------------------------------------- | ||
| function cvlFunctionThatMayRevert(bool input) { | ||
| if (!input) { | ||
| revert("Input was false in CVL function"); | ||
| } | ||
| } | ||
|
|
||
| // --------------------------------------------------------- | ||
| // 3. A summary function that can revert. | ||
| // --------------------------------------------------------- | ||
| // The contract's `summarizeMe(bool b)` is mapped to `cvlSummarizeMe(b)` in CVL. | ||
| // If `b` is false, we revert with a message. When called via `callSummarizeMe@withrevert(b)`, | ||
| // any revert here bubbles up and sets `lastReverted`. | ||
| function cvlSummarizeMe(bool b) { | ||
| if (!b) { | ||
| revert("Summarize revert because b is false"); | ||
| } | ||
| } | ||
|
|
||
| // --------------------------------------------------------- | ||
| // 4. Example rule: calling 'foo' with @withrevert | ||
| // --------------------------------------------------------- | ||
| rule testFooWithRevert { | ||
| bool condition; | ||
| // Using @withrevert to capture whether 'foo' reverts | ||
| foo@withrevert(condition); | ||
|
|
||
| // 'foo' reverts when 'condition' is false | ||
| assert lastReverted <=> !condition; | ||
| } | ||
|
|
||
| // --------------------------------------------------------- | ||
| // 5. Example rule: calling a CVL function with @withrevert | ||
| // --------------------------------------------------------- | ||
| rule testCvlFunctionWithRevert { | ||
| bool condition; | ||
| // If 'cvlFunctionThatMayRevert' reverts, 'lastReverted' is set to true | ||
| cvlFunctionThatMayRevert@withrevert(condition); | ||
|
|
||
| // Check that 'lastReverted' matches whether 'condition' was false | ||
| assert lastReverted <=> !condition; | ||
| } | ||
|
|
||
| // --------------------------------------------------------- | ||
| // 6. Demonstration #1 for canRevert: inline approach | ||
| // --------------------------------------------------------- | ||
| function wrapperForCanRevertInline(bool condition) returns bool { | ||
| // Here we call canRevert WITH @withrevert internally | ||
| canRevert@withrevert(condition); | ||
| return lastReverted; // We return the revert status | ||
| } | ||
|
|
||
| rule testCanRevertInline { | ||
| bool condition; | ||
| bool outcome = wrapperForCanRevertInline(condition); | ||
|
|
||
| // 'outcome' is true exactly when 'condition' is false (i.e., canRevert reverts) | ||
| assert outcome <=> !condition; | ||
| } | ||
|
|
||
| // --------------------------------------------------------- | ||
| // 7. Demonstration #2 for canRevert: revert "bubbling up" | ||
| // --------------------------------------------------------- | ||
| // We call 'canRevert(condition)' WITHOUT '@withrevert' inside the wrapper, | ||
| // letting the revert bubble up to the outer call (the rule). | ||
| function wrapperForCanRevertBubble(bool condition) { | ||
| canRevert(condition); | ||
| } | ||
|
|
||
| rule testCanRevertBubbleUp { | ||
| bool condition; | ||
| // We call the wrapper with @withrevert, so if 'canRevert' reverts, it bubbles up | ||
| wrapperForCanRevertBubble@withrevert(condition); | ||
|
|
||
| // If 'condition' is false => 'canRevert' reverts => bubble up => lastReverted = true | ||
| assert lastReverted <=> !condition; | ||
| } | ||
|
|
||
| // --------------------------------------------------------- | ||
| // 8. Summarize-Me case: the "most interesting" scenario | ||
| // --------------------------------------------------------- | ||
| // The contract function callSummarizeMe(b) calls summarizeMe(b), | ||
| // which is summarized by cvlSummarizeMe(b). That summary reverts | ||
| // if b == false. We'll use the 'rule sanity basic' flag to see the call trace. | ||
| rule testCallSummarizeMe{ | ||
| bool condition; | ||
| // Because summarizeMe is mapped to cvlSummarizeMe in CVL (which can revert), | ||
| // calling callSummarizeMe with @withrevert will set lastReverted if condition == false. | ||
| callSummarizeMe@withrevert(condition); | ||
|
|
||
| // If condition is false => revert => lastReverted = true | ||
| // If condition is true => no revert => lastReverted = false | ||
| assert lastReverted <=> !condition; | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we have a summzrize for this function that reverts? and then a rule for callSummarizeMe . this is the most interesting case.
Have the rule with sanity true, so we see how the call trace looks like
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by sanity true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done