Add Halmos support for formal verification (#5034)

Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
This commit is contained in:
Ernesto García
2024-05-23 09:01:12 -06:00
committed by GitHub
parent 9de916dd9c
commit f1a69f164e
17 changed files with 197 additions and 38 deletions

View File

@ -6,17 +6,26 @@ const header = `\
pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol";
import {SymTest} from "halmos-cheatcodes/SymTest.sol";
import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol";
`;
const array = `\
bytes[] private _array;
function symbolicDeriveArray(uint256 length, uint256 offset) public {
vm.assume(length > 0);
vm.assume(offset < length);
_assertDeriveArray(length, offset);
}
function testDeriveArray(uint256 length, uint256 offset) public {
length = bound(length, 1, type(uint256).max);
offset = bound(offset, 0, length - 1);
_assertDeriveArray(length, offset);
}
function _assertDeriveArray(uint256 length, uint256 offset) public {
bytes32 baseSlot;
assembly {
baseSlot := _array.slot
@ -33,10 +42,10 @@ function testDeriveArray(uint256 length, uint256 offset) public {
}
`;
const mapping = ({ type, name, isValueType }) => `\
const mapping = ({ type, name }) => `\
mapping(${type} => bytes) private _${type}Mapping;
function testDeriveMapping${name}(${type} ${isValueType ? '' : 'memory'} key) public {
function testSymbolicDeriveMapping${name}(${type} key) public {
bytes32 baseSlot;
assembly {
baseSlot := _${type}Mapping.slot
@ -52,10 +61,37 @@ function testDeriveMapping${name}(${type} ${isValueType ? '' : 'memory'} key) pu
}
`;
const boundedMapping = ({ type, name }) => `\
mapping(${type} => bytes) private _${type}Mapping;
function testDeriveMapping${name}(${type} memory key) public {
_assertDeriveMapping${name}(key);
}
function symbolicDeriveMapping${name}() public {
_assertDeriveMapping${name}(svm.create${name}(256, "DeriveMapping${name}Input"));
}
function _assertDeriveMapping${name}(${type} memory key) internal {
bytes32 baseSlot;
assembly {
baseSlot := _${type}Mapping.slot
}
bytes storage derived = _${type}Mapping[key];
bytes32 derivedSlot;
assembly {
derivedSlot := derived.slot
}
assertEq(baseSlot.deriveMapping(key), derivedSlot);
}
`;
// GENERATE
module.exports = format(
header.trimEnd(),
'contract SlotDerivationTest is Test {',
'contract SlotDerivationTest is Test, SymTest {',
'using SlotDerivation for bytes32;',
'',
array,
@ -68,6 +104,6 @@ module.exports = format(
isValueType: type.isValueType,
})),
),
).map(type => mapping(type)),
).map(type => (type.isValueType ? mapping(type) : boundedMapping(type))),
'}',
);