|
| 1 | +# Data Invariant Example |
| 2 | + |
| 3 | +This folder shows a **simplified** demonstration of the new `requireInvariant` semantics in Certora’s CVL. We have: |
| 4 | + |
| 5 | +1. A **Solidity contract** (`DataInvariant.sol`) that allows negative balances to appear. |
| 6 | +2. A **CVL specification** (`DataInvariant.spec`) that declares an invariant requiring **nonnegative** balances. |
| 7 | +3. A **configuration file** (`DataInvariant.conf`) used to run the Certora Prover with our spec. |
| 8 | + |
| 9 | +> **Note:** The invariant is not enforced at the time the hook is triggered in versions **before 8**, but it is enforced globally in versions **from 8 on**. |
| 10 | +
|
| 11 | +## Overview |
| 12 | + |
| 13 | +- **`DataInvariant.sol`** |
| 14 | + A contract that tracks `balance[a]` for each address and includes the `breakInvariant` function. |
| 15 | + |
| 16 | +- **`DataInvariant.spec`** |
| 17 | + - **Invariant**: `alwaysPositive(address a)` states `currentContract.balance[a] >= 0`. |
| 18 | + The invariant is violated when `breakInvariant` is called under the semantics from version 8 on but not under the semantics before version 8. |
| 19 | + - **Hook**: Whenever the contract reads `accessInvariant[user]` (i.e., an SLOAD on `accessInvariant[user]`), we call a CVL function `safeAssumptions(user)` which has a `requireInvariant alwaysPositive(user)`. |
| 20 | + |
| 21 | +**Explanation of `requireInvariant` semantics:** |
| 22 | +- In versions **before 8**, `requireInvariant` was only enforced at the specific location where it was used (inside the hook, when `accessInvariant[user]` is read). This means the invariant is not guaranteed to hold globally—only at the hook point. |
| 23 | + - a) Negative balances can exist during a transaction, as long as the invariant holds at the hook location. |
| 24 | +- In versions **from 8 on**, the `requireInvariant` command is enforced globally—meaning the invariant must hold before any code of a rule or invariant is executed, not just at the hook. |
| 25 | + - b) Negative balances are never allowed at any point where the invariant is expected to hold, so the violation is detected. |
| 26 | + |
| 27 | +## How to Run |
| 28 | + |
| 29 | +1. **Command** the Solidity file and run the Certora Prover using the provided config: |
| 30 | + ```bash |
| 31 | + certoraRun DataInvariant.conf |
| 32 | + ``` |
| 33 | + |
| 34 | +## Execution Before Version 8 |
| 35 | +See the Certora Prover output: |
| 36 | +https://vaas-stg.certora.com/output/1512/b774667137d348039ab86be0f3a1b8f8?anonymousKey=cbd717f02b7ea78a275518c7c4ae340282789318 |
| 37 | +- Expected outcome: The invariant is only checked at the hook, so negative balances can exist temporarily during a transaction, leading to incorrect assumptions about the state of the contract. |
| 38 | + |
| 39 | +## Execution From Version 8 On |
| 40 | +See the Certora Prover output: |
| 41 | +https://vaas-stg.certora.com/output/1512/09d15b32556c469f9173b33b41e01711?anonymousKey=c1369211cc8d991e1f345d9012ef655dfb495d05 |
| 42 | +- Expected outcome: The invariant is enforced globally, so any negative balance is detected as a violation, ensuring the contract state always satisfies the invariant. |
0 commit comments