Skip to content

Commit 6baef79

Browse files
Transpile f1a69f16
1 parent 18ff168 commit 6baef79

File tree

18 files changed

+198
-39
lines changed

18 files changed

+198
-39
lines changed

.github/actions/gas-compare/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
name: Compare gas costs
2+
description: Compare gas costs between branches
23
inputs:
34
token:
45
description: github token

.github/actions/setup/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
name: Setup
2+
description: Common environment setup
23

34
runs:
45
using: composite

.github/actions/storage-layout/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
name: Compare storage layouts
2+
description: Compare storage layouts between branches
23
inputs:
34
token:
45
description: github token

.github/workflows/formal-verification.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ jobs:
4848
with:
4949
python-version: ${{ env.PIP_VERSION }}
5050
cache: 'pip'
51+
cache-dependency-path: 'fv-requirements.txt'
5152
- name: Install python packages
52-
run: pip install -r requirements.txt
53+
run: pip install -r fv-requirements.txt
5354
- name: Install java
5455
uses: actions/setup-java@v3
5556
with:
@@ -66,3 +67,20 @@ jobs:
6667
node certora/run.js ${{ steps.arguments.outputs.result }} >> "$GITHUB_STEP_SUMMARY"
6768
env:
6869
CERTORAKEY: ${{ secrets.CERTORAKEY }}
70+
71+
halmos:
72+
runs-on: ubuntu-latest
73+
steps:
74+
- uses: actions/checkout@v4
75+
- name: Set up environment
76+
uses: ./.github/actions/setup
77+
- name: Install python
78+
uses: actions/setup-python@v5
79+
with:
80+
python-version: ${{ env.PIP_VERSION }}
81+
cache: 'pip'
82+
cache-dependency-path: 'fv-requirements.txt'
83+
- name: Install python packages
84+
run: pip install -r fv-requirements.txt
85+
- name: Run Halmos
86+
run: halmos --match-test '^symbolic|^testSymbolic' -vv

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
[submodule "lib/erc4626-tests"]
66
path = lib/erc4626-tests
77
url = https://github.com/a16z/erc4626-tests.git
8+
[submodule "lib/halmos-cheatcodes"]
9+
path = lib/halmos-cheatcodes
10+
url = https://github.com/a16z/halmos-cheatcodes
811
[submodule "lib/openzeppelin-contracts"]
912
path = lib/openzeppelin-contracts
1013
url = https://github.com/OpenZeppelin/openzeppelin-contracts.git

fv-requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
certora-cli==4.13.1
2+
# File uses a custom name (fv-requirements.txt) so that it isn't picked by Netlify's build
3+
# whose latest Python version is 0.3.8, incompatible with most recent versions of Halmos
4+
halmos==0.1.12

lib/halmos-cheatcodes

Submodule halmos-cheatcodes added at c0d8655

requirements.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

scripts/generate/templates/SlotDerivation.t.js

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,26 @@ const header = `\
66
pragma solidity ^0.8.20;
77
88
import {Test} from "forge-std/Test.sol";
9-
9+
import {SymTest} from "halmos-cheatcodes/SymTest.sol";
1010
import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol";
1111
`;
1212

1313
const array = `\
1414
bytes[] private _array;
1515
16+
function symbolicDeriveArray(uint256 length, uint256 offset) public {
17+
vm.assume(length > 0);
18+
vm.assume(offset < length);
19+
_assertDeriveArray(length, offset);
20+
}
21+
1622
function testDeriveArray(uint256 length, uint256 offset) public {
1723
length = bound(length, 1, type(uint256).max);
1824
offset = bound(offset, 0, length - 1);
25+
_assertDeriveArray(length, offset);
26+
}
1927
28+
function _assertDeriveArray(uint256 length, uint256 offset) public {
2029
bytes32 baseSlot;
2130
assembly {
2231
baseSlot := _array.slot
@@ -33,10 +42,10 @@ function testDeriveArray(uint256 length, uint256 offset) public {
3342
}
3443
`;
3544

36-
const mapping = ({ type, name, isValueType }) => `\
45+
const mapping = ({ type, name }) => `\
3746
mapping(${type} => bytes) private _${type}Mapping;
3847
39-
function testDeriveMapping${name}(${type} ${isValueType ? '' : 'memory'} key) public {
48+
function testSymbolicDeriveMapping${name}(${type} key) public {
4049
bytes32 baseSlot;
4150
assembly {
4251
baseSlot := _${type}Mapping.slot
@@ -52,10 +61,37 @@ function testDeriveMapping${name}(${type} ${isValueType ? '' : 'memory'} key) pu
5261
}
5362
`;
5463

64+
const boundedMapping = ({ type, name }) => `\
65+
mapping(${type} => bytes) private _${type}Mapping;
66+
67+
function testDeriveMapping${name}(${type} memory key) public {
68+
_assertDeriveMapping${name}(key);
69+
}
70+
71+
function symbolicDeriveMapping${name}() public {
72+
_assertDeriveMapping${name}(svm.create${name}(256, "DeriveMapping${name}Input"));
73+
}
74+
75+
function _assertDeriveMapping${name}(${type} memory key) internal {
76+
bytes32 baseSlot;
77+
assembly {
78+
baseSlot := _${type}Mapping.slot
79+
}
80+
81+
bytes storage derived = _${type}Mapping[key];
82+
bytes32 derivedSlot;
83+
assembly {
84+
derivedSlot := derived.slot
85+
}
86+
87+
assertEq(baseSlot.deriveMapping(key), derivedSlot);
88+
}
89+
`;
90+
5591
// GENERATE
5692
module.exports = format(
5793
header.trimEnd(),
58-
'contract SlotDerivationTest is Test {',
94+
'contract SlotDerivationTest is Test, SymTest {',
5995
'using SlotDerivation for bytes32;',
6096
'',
6197
array,
@@ -68,6 +104,6 @@ module.exports = format(
68104
isValueType: type.isValueType,
69105
})),
70106
),
71-
).map(type => mapping(type)),
107+
).map(type => (type.isValueType ? mapping(type) : boundedMapping(type))),
72108
'}',
73109
);

0 commit comments

Comments
 (0)