Account.spec 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. import "helpers/helpers.spec";
  2. import "methods/IAccount.spec";
  3. methods {
  4. function getDataSelector(bytes) external returns (bytes4) envfree;
  5. function getFallbackHandler(bytes4) external returns (address) envfree;
  6. function _validatorLength() external returns (uint256) envfree;
  7. function _validatorContains(address) external returns (bool) envfree;
  8. function _validatorAt(uint256) external returns (address) envfree;
  9. function _validatorAtFull(uint256) external returns (bytes32) envfree;
  10. function _validatorPositionOf(address) external returns (uint256) envfree;
  11. function _executorLength() external returns (uint256) envfree;
  12. function _executorContains(address) external returns (bool) envfree;
  13. function _executorAt(uint256) external returns (address) envfree;
  14. function _executorAtFull(uint256) external returns (bytes32) envfree;
  15. function _executorPositionOf(address) external returns (uint256) envfree;
  16. function _.onInstall(bytes) external => NONDET;
  17. function _.onUninstall(bytes) external => NONDET;
  18. }
  19. definition VALIDATOR_TYPE returns uint256 = 1;
  20. definition EXECUTOR_TYPE returns uint256 = 2;
  21. definition FALLBACK_TYPE returns uint256 = 3;
  22. definition isEntryPoint(env e) returns bool =
  23. e.msg.sender == entryPoint();
  24. definition isEntryPointOrSelf(env e) returns bool =
  25. isEntryPoint(e) || e.msg.sender == currentContract;
  26. definition isExecutionModule(env e, bytes context) returns bool =
  27. isModuleInstalled(EXECUTOR_TYPE(), e.msg.sender, context);
  28. /*
  29. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  30. │ Storage consistency │
  31. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  32. */
  33. definition moduleLengthSanity() returns bool =
  34. _validatorLength() < max_uint256 && _executorLength() < max_uint256;
  35. // Dirty upper bits could cause issues. We need to prove stored values are clean
  36. invariant cleanStorageValidator(uint256 index)
  37. index < _validatorLength() => to_bytes32(_validatorAt(index)) == _validatorAtFull(index)
  38. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  39. {
  40. preserved {
  41. requireInvariant cleanStorageValidator(require_uint256(_validatorLength() - 1));
  42. }
  43. }
  44. // Dirty upper bits could cause issues. We need to prove stored values are clean
  45. invariant cleanStorageExecutor(uint256 index)
  46. index < _executorLength() => to_bytes32(_executorAt(index)) == _executorAtFull(index)
  47. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  48. {
  49. preserved {
  50. requireInvariant cleanStorageExecutor(require_uint256(_executorLength() - 1));
  51. }
  52. }
  53. invariant indexedContainedValidator(uint256 index)
  54. index < _validatorLength() => _validatorContains(_validatorAt(index))
  55. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  56. {
  57. preserved {
  58. requireInvariant consistencyIndexValidator(index);
  59. requireInvariant consistencyIndexValidator(require_uint256(_validatorLength() - 1));
  60. }
  61. }
  62. invariant indexedContainedExecutor(uint256 index)
  63. index < _executorLength() => _executorContains(_executorAt(index))
  64. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  65. {
  66. preserved {
  67. requireInvariant consistencyIndexExecutor(index);
  68. requireInvariant consistencyIndexExecutor(require_uint256(_executorLength() - 1));
  69. }
  70. }
  71. invariant atUniquenessValidator(uint256 index1, uint256 index2)
  72. (index1 < _validatorLength() && index2 < _validatorLength()) =>
  73. (index1 == index2 <=> _validatorAt(index1) == _validatorAt(index2))
  74. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  75. {
  76. preserved {
  77. requireInvariant consistencyIndexValidator(index1);
  78. requireInvariant consistencyIndexValidator(index2);
  79. }
  80. preserved uninstallModule(uint256 moduleTypeId, address module, bytes deInitData) with (env e) {
  81. requireInvariant atUniquenessValidator(index1, require_uint256(_validatorLength() - 1));
  82. requireInvariant atUniquenessValidator(index2, require_uint256(_validatorLength() - 1));
  83. }
  84. }
  85. invariant atUniquenessExecutor(uint256 index1, uint256 index2)
  86. (index1 < _executorLength() && index2 < _executorLength()) =>
  87. (index1 == index2 <=> _executorAt(index1) == _executorAt(index2))
  88. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  89. {
  90. preserved {
  91. requireInvariant consistencyIndexExecutor(index1);
  92. requireInvariant consistencyIndexExecutor(index2);
  93. }
  94. preserved uninstallModule(uint256 moduleTypeId, address module, bytes deInitData) with (env e) {
  95. requireInvariant atUniquenessExecutor(index1, require_uint256(_executorLength() - 1));
  96. requireInvariant atUniquenessExecutor(index2, require_uint256(_executorLength() - 1));
  97. }
  98. }
  99. invariant consistencyIndexValidator(uint256 index)
  100. index < _validatorLength() => _validatorPositionOf(_validatorAt(index)) == require_uint256(index + 1)
  101. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  102. {
  103. preserved uninstallModule(uint256 moduleTypeId, address otherModule, bytes deInitData) with (env e) {
  104. requireInvariant consistencyIndexValidator(require_uint256(_validatorLength() - 1));
  105. requireInvariant cleanStorageValidator(require_uint256(_validatorLength() - 1));
  106. }
  107. }
  108. invariant consistencyIndexExecutor(uint256 index)
  109. index < _executorLength() => _executorPositionOf(_executorAt(index)) == require_uint256(index + 1)
  110. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  111. {
  112. preserved uninstallModule(uint256 moduleTypeId, address otherModule, bytes deInitData) with (env e) {
  113. requireInvariant consistencyIndexExecutor(require_uint256(_executorLength() - 1));
  114. requireInvariant cleanStorageExecutor(require_uint256(_executorLength() - 1));
  115. }
  116. }
  117. invariant consistencyKeyValidator(address module)
  118. _validatorContains(module) => (
  119. _validatorPositionOf(module) > 0 &&
  120. _validatorPositionOf(module) <= _validatorLength() &&
  121. _validatorAt(require_uint256(_validatorPositionOf(module) - 1)) == module
  122. )
  123. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  124. {
  125. preserved {
  126. require moduleLengthSanity();
  127. }
  128. preserved uninstallModule(uint256 moduleTypeId, address otherModule, bytes deInitData) with (env e) {
  129. requireInvariant consistencyKeyValidator(otherModule);
  130. requireInvariant atUniquenessValidator(
  131. require_uint256(_validatorPositionOf(module) - 1),
  132. require_uint256(_validatorPositionOf(otherModule) - 1)
  133. );
  134. requireInvariant cleanStorageValidator(require_uint256(_validatorLength() - 1));
  135. }
  136. }
  137. invariant consistencyKeyExecutor(address module)
  138. _executorContains(module) => (
  139. _executorPositionOf(module) > 0 &&
  140. _executorPositionOf(module) <= _executorLength() &&
  141. _executorAt(require_uint256(_executorPositionOf(module) - 1)) == module
  142. )
  143. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  144. {
  145. preserved {
  146. require moduleLengthSanity();
  147. }
  148. preserved uninstallModule(uint256 moduleTypeId, address otherModule, bytes deInitData) with (env e) {
  149. requireInvariant consistencyKeyExecutor(otherModule);
  150. requireInvariant atUniquenessExecutor(
  151. require_uint256(_executorPositionOf(module) - 1),
  152. require_uint256(_executorPositionOf(otherModule) - 1)
  153. );
  154. requireInvariant cleanStorageExecutor(require_uint256(_executorLength() - 1));
  155. }
  156. }
  157. invariant absentValidatorIsNotStored(address module, uint256 index)
  158. index < _validatorLength() => (!_validatorContains(module) => _validatorAt(index) != module)
  159. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  160. {
  161. preserved uninstallModule(uint256 moduleTypeId, address otherModule, bytes deInitData) with (env e) {
  162. requireInvariant consistencyIndexValidator(index);
  163. requireInvariant consistencyKeyValidator(module);
  164. requireInvariant atUniquenessValidator(index, require_uint256(_validatorLength() - 1));
  165. requireInvariant cleanStorageValidator(require_uint256(_validatorLength() - 1));
  166. }
  167. }
  168. invariant absentExecutorIsNotStored(address module, uint256 index)
  169. index < _executorLength() => (!_executorContains(module) => _executorAt(index) != module)
  170. filtered { f -> f.selector != sig:execute(bytes32,bytes).selector && f.selector != sig:executeFromExecutor(bytes32,bytes).selector }
  171. {
  172. preserved uninstallModule(uint256 moduleTypeId, address otherModule, bytes deInitData) with (env e) {
  173. requireInvariant consistencyIndexExecutor(index);
  174. requireInvariant consistencyKeyExecutor(module);
  175. requireInvariant atUniquenessExecutor(index, require_uint256(_executorLength() - 1));
  176. requireInvariant cleanStorageExecutor(require_uint256(_executorLength() - 1));
  177. }
  178. }
  179. /*
  180. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  181. │ Module management │
  182. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  183. */
  184. // This guarantees that at most one fallback module is active for a given initData (i.e. selector)
  185. rule fallbackModule(address module, bytes initData) {
  186. assert isModuleInstalled(FALLBACK_TYPE(), module, initData) <=> getFallbackHandler(getDataSelector(initData)) == module;
  187. }
  188. rule moduleManagementRule(env e, method f, calldataarg args, uint256 moduleTypeId, address module, bytes additionalContext)
  189. filtered { f -> !f.isView }
  190. {
  191. bytes context;
  192. require context.length == 0;
  193. bool isEntryPoint = isEntryPoint(e);
  194. bool isEntryPointOrSelf = isEntryPointOrSelf(e);
  195. bool isExecutionModule = isExecutionModule(e, context);
  196. bool isModuleInstalledBefore = isModuleInstalled(moduleTypeId, module, additionalContext);
  197. f(e, args);
  198. bool isModuleInstalledAfter = isModuleInstalled(moduleTypeId, module, additionalContext);
  199. assert (
  200. isModuleInstalledBefore != isModuleInstalledAfter
  201. ) => (
  202. (
  203. f.selector == sig:execute(bytes32,bytes).selector &&
  204. isEntryPointOrSelf
  205. ) || (
  206. f.selector == sig:executeFromExecutor(bytes32,bytes).selector &&
  207. isExecutionModule
  208. ) || (
  209. f.selector == sig:installModule(uint256,address,bytes).selector &&
  210. isEntryPointOrSelf
  211. ) || (
  212. f.selector == sig:uninstallModule(uint256,address,bytes).selector &&
  213. isEntryPointOrSelf
  214. )
  215. );
  216. }
  217. rule installModuleRule(env e, uint256 moduleTypeId, address module, bytes initData) {
  218. uint256 otherModuleTypeId;
  219. address otherModule;
  220. bytes otherInitData;
  221. require moduleLengthSanity();
  222. requireInvariant consistencyKeyExecutor(module);
  223. requireInvariant consistencyKeyValidator(module);
  224. requireInvariant consistencyKeyExecutor(otherModule);
  225. requireInvariant consistencyKeyValidator(otherModule);
  226. bool isModuleInstalledBefore = isModuleInstalled(moduleTypeId, module, initData);
  227. bool isOtherModuleInstalledBefore = isModuleInstalled(otherModuleTypeId, otherModule, otherInitData);
  228. installModule(e, moduleTypeId, module, initData);
  229. bool isModuleInstalledAfter = isModuleInstalled(moduleTypeId, module, initData);
  230. bool isOtherModuleInstalledAfter = isModuleInstalled(otherModuleTypeId, otherModule, otherInitData);
  231. // Module is installed correctly
  232. assert !isModuleInstalledBefore && isModuleInstalledAfter;
  233. // No side effect on other modules
  234. assert isOtherModuleInstalledBefore != isOtherModuleInstalledAfter => (
  235. (
  236. moduleTypeId == otherModuleTypeId &&
  237. module == otherModule
  238. ) || (
  239. moduleTypeId == FALLBACK_TYPE() &&
  240. otherModuleTypeId == FALLBACK_TYPE() &&
  241. otherModule == 0 && // when a fallback module is installed, the 0 module is "removed" for that selector
  242. getDataSelector(otherInitData) == getDataSelector(initData) &&
  243. isOtherModuleInstalledBefore &&
  244. !isOtherModuleInstalledAfter
  245. )
  246. );
  247. }
  248. rule uninstallModuleRule(env e, uint256 moduleTypeId, address module, bytes initData) {
  249. uint256 otherModuleTypeId;
  250. address otherModule;
  251. bytes otherInitData;
  252. requireInvariant consistencyKeyExecutor(module);
  253. requireInvariant consistencyKeyValidator(module);
  254. requireInvariant consistencyKeyExecutor(otherModule);
  255. requireInvariant consistencyKeyValidator(otherModule);
  256. requireInvariant consistencyIndexExecutor(require_uint256(_executorLength() - 1));
  257. requireInvariant consistencyIndexValidator(require_uint256(_validatorLength() - 1));
  258. bool isModuleInstalledBefore = isModuleInstalled(moduleTypeId, module, initData);
  259. bool isOtherModuleInstalledBefore = isModuleInstalled(otherModuleTypeId, otherModule, otherInitData);
  260. uninstallModule(e, moduleTypeId, module, initData);
  261. bool isModuleInstalledAfter = isModuleInstalled(moduleTypeId, module, initData);
  262. bool isOtherModuleInstalledAfter = isModuleInstalled(otherModuleTypeId, otherModule, otherInitData);
  263. // Module is installed correctly
  264. assert isModuleInstalledBefore && !isModuleInstalledAfter;
  265. // No side effect on other modules
  266. assert isOtherModuleInstalledBefore != isOtherModuleInstalledAfter => (
  267. (
  268. moduleTypeId == otherModuleTypeId &&
  269. module == otherModule
  270. ) || (
  271. moduleTypeId == FALLBACK_TYPE() &&
  272. otherModuleTypeId == FALLBACK_TYPE() &&
  273. otherModule == 0 && // when a fallback module is uninstalled, the 0 module is "added" for that selector
  274. getDataSelector(otherInitData) == getDataSelector(initData) &&
  275. !isOtherModuleInstalledBefore &&
  276. isOtherModuleInstalledAfter
  277. )
  278. );
  279. }
  280. /*
  281. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  282. │ CALL OPCODE │
  283. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  284. */
  285. persistent ghost mathint calls;
  286. persistent ghost address lastcall_target;
  287. persistent ghost uint32 lastcall_selector;
  288. persistent ghost uint256 lastcall_value;
  289. persistent ghost uint256 lastcall_argsLength;
  290. hook CALL(uint256 gas, address target, uint256 value, uint256 argsOffset, uint256 argsLength, uint256 retOffset, uint256 retLength) uint256 rc {
  291. if (executingContract == currentContract) {
  292. calls = calls + 1;
  293. lastcall_target = target;
  294. lastcall_selector = selector;
  295. lastcall_value = value;
  296. lastcall_argsLength = argsLength;
  297. }
  298. }
  299. rule callOpcodeRule(env e, method f, calldataarg args)
  300. filtered { f -> !f.isView }
  301. {
  302. require calls == 0;
  303. bytes context;
  304. require context.length == 0;
  305. bool isEntryPoint = isEntryPoint(e);
  306. bool isEntryPointOrSelf = isEntryPointOrSelf(e);
  307. bool isExecutionModule = isExecutionModule(e, context);
  308. f(e, args);
  309. assert calls > 0 => (
  310. (
  311. // Can call any target with any data and value
  312. f.selector == sig:execute(bytes32,bytes).selector &&
  313. isEntryPointOrSelf
  314. ) || (
  315. // Can call any target with any data and value
  316. f.selector == sig:executeFromExecutor(bytes32,bytes).selector &&
  317. isExecutionModule
  318. ) || (
  319. // Can only call a module without any value. Target is verified by `callInstallModule`.
  320. f.selector == sig:installModule(uint256,address,bytes).selector &&
  321. isEntryPointOrSelf &&
  322. calls == 1 &&
  323. lastcall_selector == 0x6d61fe70 && // onInstall(bytes)
  324. lastcall_value == 0
  325. ) || (
  326. // Can only call a module without any value. Target is verified by `callInstallModule`.
  327. f.selector == sig:uninstallModule(uint256,address,bytes).selector &&
  328. isEntryPointOrSelf &&
  329. calls == 1 &&
  330. lastcall_selector == 0x8a91b0e3 && // onUninstall(bytes)
  331. lastcall_value == 0
  332. ) || (
  333. // Can send payment to the entrypoint or perform an external signature verification.
  334. f.selector == sig:validateUserOp(Account.PackedUserOperation,bytes32,uint256).selector &&
  335. isEntryPoint &&
  336. calls <= 2 &&
  337. (
  338. (
  339. // payPrefund (target is entryPoint and argsLength is 0)
  340. lastcall_target == entryPoint() &&
  341. lastcall_value > 0 &&
  342. lastcall_argsLength == 0
  343. ) || (
  344. // isValidSignatureWithSender (target is as validation module)
  345. isModuleInstalled(VALIDATOR_TYPE(), lastcall_target, context) &&
  346. lastcall_selector == 0x97003203 && // validateUserOp(Account.PackedUserOperation,bytes32)
  347. lastcall_value == 0
  348. )
  349. )
  350. ) || (
  351. // Arbitrary fallback, to the correct fallback handler
  352. f.isFallback &&
  353. calls == 1 &&
  354. lastcall_target == getFallbackHandler(to_bytes4(lastcall_selector)) &&
  355. lastcall_value == e.msg.value
  356. )
  357. );
  358. }
  359. rule callInstallModule(env e, uint256 moduleTypeId, address module, bytes initData) {
  360. require calls == 0;
  361. installModule(e, moduleTypeId, module, initData);
  362. assert calls == 1;
  363. assert lastcall_target == module;
  364. assert lastcall_selector == 0x6d61fe70; // onInstall(bytes)
  365. assert lastcall_value == 0;
  366. }
  367. rule callUninstallModule(env e, uint256 moduleTypeId, address module, bytes deInitData) {
  368. require calls == 0;
  369. uninstallModule(e, moduleTypeId, module, deInitData);
  370. assert calls == 1;
  371. assert lastcall_target == module;
  372. assert lastcall_selector == 0x8a91b0e3; // onUninstall(bytes)
  373. assert lastcall_value == 0;
  374. }
  375. /*
  376. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  377. │ DELEGATECALL OPCODE │
  378. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  379. */
  380. persistent ghost mathint delegatecalls;
  381. persistent ghost address lastdelegatecall_target;
  382. persistent ghost uint32 lastdelegatecall_selector;
  383. persistent ghost uint256 lastdelegatecall_argsLength;
  384. hook DELEGATECALL(uint256 gas, address target, uint256 argsOffset, uint256 argsLength, uint256 retOffset, uint256 retLength) uint256 rc {
  385. if (executingContract == currentContract) {
  386. delegatecalls = delegatecalls + 1;
  387. lastdelegatecall_target = target;
  388. lastdelegatecall_selector = selector;
  389. lastdelegatecall_argsLength = argsLength;
  390. }
  391. }
  392. rule delegatecallOpcodeRule(env e, method f, calldataarg args)
  393. filtered { f -> !f.isView }
  394. {
  395. require delegatecalls == 0;
  396. bytes context;
  397. require context.length == 0;
  398. bool isEntryPoint = isEntryPoint(e);
  399. bool isEntryPointOrSelf = isEntryPointOrSelf(e);
  400. bool isExecutionModule = isExecutionModule(e, context);
  401. f(e, args);
  402. assert delegatecalls > 0 => (
  403. (
  404. // Can delegatecall to target with any data
  405. f.selector == sig:execute(bytes32,bytes).selector &&
  406. isEntryPointOrSelf
  407. ) || (
  408. // Can delegatecall to target with any data
  409. f.selector == sig:executeFromExecutor(bytes32,bytes).selector &&
  410. isExecutionModule
  411. )
  412. );
  413. }
  414. /*
  415. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  416. │ ERC-4337 │
  417. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  418. */
  419. rule validateUserOpPayment(env e){
  420. Account.PackedUserOperation userOp;
  421. bytes32 userOpHash;
  422. uint256 missingAccountFunds;
  423. uint256 balanceBefore = nativeBalances[currentContract];
  424. validateUserOp(e, userOp, userOpHash, missingAccountFunds);
  425. uint256 balanceAfter = nativeBalances[currentContract];
  426. assert balanceBefore - balanceAfter <= missingAccountFunds;
  427. }