-
Notifications
You must be signed in to change notification settings - Fork 43
Required invariant minimal example #166
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"files": ["DataInvariant.sol"], | ||
"verify": "DataInvariant:DataInvariant.spec", | ||
"solc": "solc8.25", | ||
"msg": "Verifying data invariants" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
/** | ||
* @title DataInvariant | ||
* @dev A contract with a per-address balance array and a function that can break an intended invariant. | ||
*/ | ||
contract DataInvariant { | ||
|
||
mapping(address => int256) public balance; | ||
mapping(address => bool) public accessInvariant; | ||
|
||
/** | ||
* @notice Decreases the address's balance by 2*value and then partially restores it, | ||
* potentially leaving it negative if `balance[a]` wasn't large enough. | ||
*/ | ||
function breakInvariant(address a, int256 value) external returns (bool accessInv) { | ||
require(value >= 0, "Value must be nonnegative"); | ||
balance[a] -= 2 * value; // Force a large negative change | ||
accessInv = accessInvariant[a]; // Read from the 'accessInvariant' mapping | ||
balance[a] += value; // Restore half, potentially leaving negative | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// -------------------------------------------------------- | ||
// 1. Declare the methods we want to call or inspect | ||
// -------------------------------------------------------- | ||
methods { | ||
function breakInvariant(address, int256) external returns bool envfree; | ||
function accessInvariant(address) external returns bool envfree; | ||
} | ||
|
||
// -------------------------------------------------------- | ||
// 2. Define the invariant: alwaysPositive **should be violated** with the new semantics but should be verified with the old semantics | ||
// -------------------------------------------------------- | ||
invariant alwaysPositive(address a) | ||
currentContract.balance[a] >= 0; | ||
|
||
// -------------------------------------------------------- | ||
// 3. A helper function that enforces the invariant | ||
// -------------------------------------------------------- | ||
function safeAssumptions(address user) { | ||
requireInvariant alwaysPositive(user); | ||
} | ||
|
||
// -------------------------------------------------------- | ||
// 4. Hook: whenever we read `accessInvariant[user]`, call `safeAssumptions(user)` | ||
// -------------------------------------------------------- | ||
hook Sload bool b currentContract.accessInvariant[KEY address user] { | ||
safeAssumptions(user); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Data Invariant Example | ||
|
||
This folder shows a **simplified** demonstration of the new `requireInvariant` semantics in Certora’s CVL. We have: | ||
|
||
1. A **Solidity contract** (`DataInvariant.sol`) that allows negative balances to appear. | ||
2. A **CVL specification** (`DataInvariant.spec`) that declares an invariant requiring **nonnegative** balances. | ||
3. A **configuration file** (`DataInvariant.conf`) used to run the Certora Prover with our spec. | ||
|
||
Under the **old** semantics, the invariant won't be enforced at the time the hook triggered, leading to a **false passing** invariant execution. Under the **new** `requireInvariant` semantics, the invariant is checked at rule boundaries or after calls/havocs for strong invariants, correctly **failing** when a negative balance occurs. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we say which version of certora? the requireinvariant is checked at the rule boundaries on the arguments passed... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new version isn't published yet so i am not sure how to mention specific version here, @yoav-el-certora maybe you know how we should handle this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once @johspaeth merge his PR, we will know the release version for this example. (Both this PR and the other one should not merge before Johannes merge his PR) |
||
|
||
## Overview | ||
|
||
- **`DataInvariant.sol`** | ||
A contract that tracks `balance[a]` for each address and includes the `breakInvariant` function. | ||
|
||
- **`DataInvariant.spec`** | ||
- **Invariant**: `alwaysPositive(address a)` states `currentContract.balance[a] >= 0`. | ||
The invariant is violated when `breakInvariant` is called under the new semantics but not under the old semantics. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of new and old use versions |
||
- **Hook**: When the contract reads `accessInvariant[user]`, we call a CVL function `safeAssumptions(user)` which has a `requireInvariant alwaysPositive(user)`. | ||
nivcertora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
## How to Run | ||
|
||
1. **Command** the Solidity file and run the Certora Prover using the provided config: | ||
```bash | ||
certoraRun DataInvariant.conf | ||
``` | ||
|
||
## Execution Under the Old Semantic | ||
See the Certora Prover output: | ||
https://vaas-stg.certora.com/output/1512/b774667137d348039ab86be0f3a1b8f8?anonymousKey=cbd717f02b7ea78a275518c7c4ae340282789318 | ||
- Expected outcome: The invariant verified incorrectly, allowing negative balances as long as they occur in the transaction but not after the transaction. This leads to incorrect assumptions about the state of the contract. | ||
|
||
## Execution Under the New Semantic | ||
See the Certora Prover output: | ||
https://vaas-stg.certora.com/output/1512/09d15b32556c469f9173b33b41e01711?anonymousKey=c1369211cc8d991e1f345d9012ef655dfb495d05 | ||
- Expected outcome: The invariant correctly violated when a negative balance arises. This leads to incorrect assumptions about the state of the contract. |
Uh oh!
There was an error while loading. Please reload this page.