Skip to content

Commit ec2c88d

Browse files
authored
test: simplify symbolic tests using new halmos cheatcode
1 parent 4020fde commit ec2c88d

File tree

5 files changed

+26
-193
lines changed

5 files changed

+26
-193
lines changed

lib/halmos-cheatcodes

test/IdRegistry/IdRegistry.symbolic.t.sol

Lines changed: 2 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ contract IdRegistrySymTest is SymTest, Test {
3939
y = svm.createAddress("y");
4040
}
4141

42-
function check_Invariants_PostMigration(bytes4 selector, address caller) public {
42+
function check_Invariants_PostMigration(address caller) public {
4343
_assumeMigrated();
4444
_initState();
4545
vm.assume(x != y);
@@ -52,7 +52,7 @@ contract IdRegistrySymTest is SymTest, Test {
5252

5353
// Execute an arbitrary tx to IdRegistry
5454
vm.prank(caller);
55-
(bool success,) = address(idRegistry).call(_calldataFor(selector));
55+
(bool success,) = address(idRegistry).call(svm.createCalldata("IdRegistry"));
5656
vm.assume(success); // ignore reverting cases
5757

5858
// Record post-state
@@ -176,84 +176,4 @@ contract IdRegistrySymTest is SymTest, Test {
176176
idRegistry.isMigrated() && block.timestamp > idRegistry.migratedAt() + idRegistry.gracePeriod();
177177
vm.assume(migrationCompleted);
178178
}
179-
180-
/**
181-
* @dev Generates valid calldata for a given function selector.
182-
*/
183-
function _calldataFor(bytes4 selector) internal returns (bytes memory) {
184-
bytes memory args;
185-
if (selector == idRegistry.transfer.selector) {
186-
args = abi.encode(svm.createAddress("to"), svm.createUint256("deadline"), svm.createBytes(65, "sig"));
187-
} else if (selector == idRegistry.transferFor.selector) {
188-
args = abi.encode(
189-
svm.createAddress("from"),
190-
svm.createAddress("to"),
191-
svm.createUint256("fromDeadline"),
192-
svm.createBytes(65, "fromSig"),
193-
svm.createUint256("toDeadline"),
194-
svm.createBytes(65, "toSig")
195-
);
196-
} else if (selector == idRegistry.transferAndChangeRecovery.selector) {
197-
args = abi.encode(
198-
svm.createAddress("to"),
199-
svm.createAddress("recovery"),
200-
svm.createUint256("deadline"),
201-
svm.createBytes(65, "sig")
202-
);
203-
} else if (selector == idRegistry.transferAndChangeRecoveryFor.selector) {
204-
args = abi.encode(
205-
svm.createAddress("from"),
206-
svm.createAddress("to"),
207-
svm.createAddress("recovery"),
208-
svm.createUint256("fromDeadline"),
209-
svm.createBytes(65, "fromSig"),
210-
svm.createUint256("toDeadline"),
211-
svm.createBytes(65, "toSig")
212-
);
213-
} else if (selector == idRegistry.changeRecoveryAddressFor.selector) {
214-
args = abi.encode(
215-
svm.createAddress("owner"),
216-
svm.createAddress("recovery"),
217-
svm.createUint256("deadline"),
218-
svm.createBytes(65, "sig")
219-
);
220-
} else if (selector == idRegistry.recover.selector) {
221-
args = abi.encode(
222-
svm.createAddress("from"),
223-
svm.createAddress("to"),
224-
svm.createUint256("deadline"),
225-
svm.createBytes(65, "sig")
226-
);
227-
} else if (selector == idRegistry.recoverFor.selector) {
228-
args = abi.encode(
229-
svm.createAddress("from"),
230-
svm.createAddress("to"),
231-
svm.createUint256("recoveryDeadline"),
232-
svm.createBytes(65, "recoverySig"),
233-
svm.createUint256("toDeadline"),
234-
svm.createBytes(65, "toSig")
235-
);
236-
} else if (selector == idRegistry.verifyFidSignature.selector) {
237-
args = abi.encode(
238-
svm.createAddress("custodyAddress"),
239-
svm.createUint256("fid"),
240-
svm.createBytes32("digest"),
241-
svm.createBytes(65, "sig")
242-
);
243-
} else if (selector == idRegistry.bulkRegisterIds.selector) {
244-
IIdRegistry.BulkRegisterData[] memory ids = new IIdRegistry.BulkRegisterData[](0);
245-
args = abi.encode(ids);
246-
} else if (selector == idRegistry.bulkRegisterIdsWithDefaultRecovery.selector) {
247-
IIdRegistry.BulkRegisterDefaultRecoveryData[] memory ids =
248-
new IIdRegistry.BulkRegisterDefaultRecoveryData[](0);
249-
args = abi.encode(ids, address(0));
250-
} else if (selector == idRegistry.bulkResetIds.selector) {
251-
uint24[] memory ids = new uint24[](0);
252-
args = abi.encode(ids);
253-
} else {
254-
args = svm.createBytes(1024, "data");
255-
}
256-
257-
return abi.encodePacked(selector, args);
258-
}
259179
}

test/KeyRegistry/KeyRegistry.symbolic.t.sol

Lines changed: 13 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {KeyRegistryHarness} from "./utils/KeyRegistryHarness.sol";
1010
import {IdRegistry} from "../../src/IdRegistry.sol";
1111
import {StubValidator} from "../Utils.sol";
1212

13+
/// @custom:halmos --default-bytes-lengths 0,32,1024,65
1314
contract KeyRegistrySymTest is SymTest, Test {
1415
IdRegistry idRegistry;
1516
address idRegistration;
@@ -81,7 +82,7 @@ contract KeyRegistrySymTest is SymTest, Test {
8182
}
8283

8384
// Verify the KeyRegistry invariants
84-
function check_Invariants(bytes4 selector, address caller) public {
85+
function check_Invariants(address caller) public {
8586
// Additional setup to cover various input states
8687
if (svm.createBool("migrate?")) {
8788
vm.prank(migrator);
@@ -107,8 +108,16 @@ contract KeyRegistrySymTest is SymTest, Test {
107108
!keyRegistry.isMigrated() || block.timestamp <= keyRegistry.migratedAt() + keyRegistry.gracePeriod();
108109

109110
// Execute an arbitrary tx to KeyRegistry
111+
bytes memory data = svm.createCalldata("KeyRegistry");
112+
bytes4 selector = bytes4(data);
113+
114+
// Link the first argument of removeFor() to the user variable so that it can be used later in assertions
115+
if (selector == keyRegistry.removeFor.selector) {
116+
vm.assume(user == address(uint160(uint256(bytes32(this.slice(data, 4, 36))))));
117+
}
118+
110119
vm.prank(caller);
111-
(bool success,) = address(keyRegistry).call(mk_calldata(selector, user));
120+
(bool success,) = address(keyRegistry).call(data);
112121
vm.assume(success); // ignore reverting cases
113122

114123
// Record post-state
@@ -170,95 +179,7 @@ contract KeyRegistrySymTest is SymTest, Test {
170179
}
171180
}
172181

173-
// Case-splitting tactic: explicitly branching into two states: cond vs !cond
174-
function split_cases(bool cond) internal pure {
175-
if (cond) return;
176-
}
177-
178-
function mk_calldata(bytes4 selector, address user) internal returns (bytes memory) {
179-
// Ignore view functions
180-
vm.assume(selector != keyRegistry.REMOVE_TYPEHASH.selector);
181-
vm.assume(selector != keyRegistry.VERSION.selector);
182-
vm.assume(selector != keyRegistry.eip712Domain.selector);
183-
vm.assume(selector != keyRegistry.gatewayFrozen.selector);
184-
vm.assume(selector != keyRegistry.gracePeriod.selector);
185-
vm.assume(selector != keyRegistry.guardians.selector);
186-
vm.assume(selector != keyRegistry.idRegistry.selector);
187-
vm.assume(selector != keyRegistry.isMigrated.selector);
188-
vm.assume(selector != keyRegistry.keyAt.selector);
189-
vm.assume(selector != keyRegistry.keyDataOf.selector);
190-
vm.assume(selector != keyRegistry.keyGateway.selector);
191-
vm.assume(selector != keyRegistry.keys.selector);
192-
vm.assume(selector != bytes4(0x1f64222f)); // keysOf
193-
vm.assume(selector != bytes4(0xf27995e3)); // keysOf paged
194-
vm.assume(selector != keyRegistry.maxKeysPerFid.selector);
195-
vm.assume(selector != keyRegistry.migratedAt.selector);
196-
vm.assume(selector != keyRegistry.migrator.selector);
197-
vm.assume(selector != keyRegistry.nonces.selector);
198-
vm.assume(selector != keyRegistry.owner.selector);
199-
vm.assume(selector != keyRegistry.paused.selector);
200-
vm.assume(selector != keyRegistry.pendingOwner.selector);
201-
vm.assume(selector != keyRegistry.totalKeys.selector);
202-
vm.assume(selector != keyRegistry.validators.selector);
203-
204-
// Create symbolic values to be included in calldata
205-
uint256 fid = svm.createUint256("fid");
206-
uint32 keyType = uint32(svm.createUint(32, "keyType"));
207-
bytes memory key = svm.createBytes(32, "key");
208-
uint8 metadataType = uint8(svm.createUint(8, "metadataType"));
209-
bytes memory metadata = svm.createBytes(32, "metadata");
210-
uint256 deadline = svm.createUint256("deadline");
211-
bytes memory sig = svm.createBytes(65, "sig");
212-
213-
// Halmos requires symbolic dynamic arrays to be given with a specific size.
214-
// In this test, we provide arrays with length 2.
215-
IKeyRegistry.BulkAddData[] memory addData = new IKeyRegistry.BulkAddData[](2);
216-
IKeyRegistry.BulkResetData[] memory resetData = new IKeyRegistry.BulkResetData[](2);
217-
218-
// New scope, stack workaround.
219-
{
220-
bytes[][] memory fidKeys = new bytes[][](2);
221-
fidKeys[0] = new bytes[](1);
222-
fidKeys[0][0] = key;
223-
224-
bytes memory key2 = svm.createBytes(32, "key2");
225-
fidKeys[1] = new bytes[](1);
226-
fidKeys[1][0] = key2;
227-
228-
uint256 fid2 = svm.createUint256("fid2");
229-
230-
IKeyRegistry.BulkAddKey[] memory keyData1 = new IKeyRegistry.BulkAddKey[](1);
231-
IKeyRegistry.BulkAddKey[] memory keyData2 = new IKeyRegistry.BulkAddKey[](1);
232-
keyData1[0] = IKeyRegistry.BulkAddKey({key: key, metadata: ""});
233-
keyData2[0] = IKeyRegistry.BulkAddKey({key: key2, metadata: ""});
234-
235-
addData[0] = IKeyRegistry.BulkAddData({fid: fid, keys: keyData1});
236-
addData[1] = IKeyRegistry.BulkAddData({fid: fid2, keys: keyData2});
237-
238-
resetData[0] = IKeyRegistry.BulkResetData({fid: fid, keys: fidKeys[0]});
239-
resetData[1] = IKeyRegistry.BulkResetData({fid: fid2, keys: fidKeys[1]});
240-
}
241-
242-
// Generate calldata based on the function selector
243-
bytes memory args;
244-
if (selector == keyRegistry.add.selector) {
245-
// Explicitly branching based on conditions.
246-
// Note: The negations of conditions are also taken into account.
247-
split_cases(keyType == uint32(1) && metadataType == uint8(1));
248-
args = abi.encode(user, keyType, key, metadataType, metadata);
249-
} else if (selector == keyRegistry.remove.selector) {
250-
args = abi.encode(key);
251-
} else if (selector == keyRegistry.removeFor.selector) {
252-
args = abi.encode(user, key, deadline, sig);
253-
} else if (selector == keyRegistry.bulkAddKeysForMigration.selector) {
254-
args = abi.encode(addData);
255-
} else if (selector == keyRegistry.bulkResetKeysForMigration.selector) {
256-
args = abi.encode(resetData);
257-
} else {
258-
// For functions where all parameters are static (not dynamic arrays or bytes),
259-
// a raw byte array is sufficient instead of explicitly specifying each argument.
260-
args = svm.createBytes(1024, "data"); // choose a size that is large enough to cover all parameters
261-
}
262-
return abi.encodePacked(selector, args);
182+
function slice(bytes calldata data, uint start, uint end) external returns (bytes memory) {
183+
return data[start:end];
263184
}
264185
}

test/abstract/Guardians/Guardians.symbolic.t.sol

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ contract GuardiansSymTest is SymTest, Test {
2727
y = svm.createAddress("y");
2828
}
2929

30-
function check_Invariants(bytes4 selector, address caller) public {
30+
function check_Invariants(address caller) public {
3131
_initState();
3232
vm.assume(x != owner);
3333
vm.assume(x != y);
@@ -37,9 +37,12 @@ contract GuardiansSymTest is SymTest, Test {
3737
bool oldGuardianX = guarded.guardians(x);
3838
bool oldGuardianY = guarded.guardians(y);
3939

40+
bytes memory data = svm.createCalldata("GuardiansExample");
41+
bytes4 selector = bytes4(data);
42+
4043
// Execute an arbitrary tx
4144
vm.prank(caller);
42-
(bool success,) = address(guarded).call(_calldataFor(selector));
45+
(bool success,) = address(guarded).call(data);
4346
vm.assume(success); // ignore reverting cases
4447

4548
// Record post-state
@@ -104,11 +107,4 @@ contract GuardiansSymTest is SymTest, Test {
104107
guarded.pause();
105108
}
106109
}
107-
108-
/**
109-
* @dev Generates valid calldata for a given function selector.
110-
*/
111-
function _calldataFor(bytes4 selector) internal returns (bytes memory) {
112-
return abi.encodePacked(selector, svm.createBytes(1024, "data"));
113-
}
114110
}

test/abstract/Migration/Migration.symbolic.t.sol

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,19 @@ contract MigrationSymTest is SymTest, Test {
2929
migration = new MigrationExample(gracePeriod, migrator, owner);
3030
}
3131

32-
function check_Invariants(bytes4 selector, address caller) public {
32+
function check_Invariants(address caller) public {
3333
_initState();
3434

3535
// Record pre-state
3636
uint40 oldMigratedAt = migration.migratedAt();
3737
address oldMigrator = migration.migrator();
3838

39+
bytes memory data = svm.createCalldata("MigrationExample");
40+
bytes4 selector = bytes4(data);
41+
3942
// Execute an arbitrary tx
4043
vm.prank(caller);
41-
(bool success,) = address(migration).call(_calldataFor(selector));
44+
(bool success,) = address(migration).call(data);
4245
vm.assume(success); // ignore reverting cases
4346

4447
// Record post-state
@@ -108,11 +111,4 @@ contract MigrationSymTest is SymTest, Test {
108111
migration.migrate();
109112
}
110113
}
111-
112-
/**
113-
* @dev Generates valid calldata for a given function selector.
114-
*/
115-
function _calldataFor(bytes4 selector) internal returns (bytes memory) {
116-
return abi.encodePacked(selector, svm.createBytes(1024, "data"));
117-
}
118114
}

0 commit comments

Comments
 (0)