EnumerableSet.spec 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import "helpers/helpers.spec";
  2. methods {
  3. // library
  4. function add(bytes32) external returns (bool) envfree;
  5. function remove(bytes32) external returns (bool) envfree;
  6. function contains(bytes32) external returns (bool) envfree;
  7. function length() external returns (uint256) envfree;
  8. function at_(uint256) external returns (bytes32) envfree;
  9. // FV
  10. function _positionOf(bytes32) external returns (uint256) envfree;
  11. }
  12. /*
  13. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  14. │ Helpers │
  15. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  16. */
  17. definition sanity() returns bool =
  18. length() < max_uint256;
  19. /*
  20. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  21. │ Invariant: All indexed keys are contained │
  22. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  23. */
  24. invariant indexedContained(uint256 index)
  25. index < length() => contains(at_(index))
  26. {
  27. preserved {
  28. requireInvariant consistencyIndex(index);
  29. requireInvariant consistencyIndex(require_uint256(length() - 1));
  30. }
  31. }
  32. /*
  33. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  34. │ Invariant: A value can only be stored at a single location │
  35. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  36. */
  37. invariant atUniqueness(uint256 index1, uint256 index2)
  38. index1 == index2 <=> at_(index1) == at_(index2)
  39. {
  40. preserved remove(bytes32 key) {
  41. requireInvariant atUniqueness(index1, require_uint256(length() - 1));
  42. requireInvariant atUniqueness(index2, require_uint256(length() - 1));
  43. }
  44. }
  45. /*
  46. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  47. │ Invariant: index <> key relationship is consistent │
  48. │ │
  49. │ Note that the two consistencyXxx invariants, put together, prove that at_ and _positionOf are inverse of one │
  50. │ another. This proves that we have a bijection between indices (the enumerability part) and keys (the entries that │
  51. │ are added and removed from the EnumerableSet). │
  52. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  53. */
  54. invariant consistencyIndex(uint256 index)
  55. index < length() => _positionOf(at_(index)) == require_uint256(index + 1)
  56. {
  57. preserved remove(bytes32 key) {
  58. requireInvariant consistencyIndex(require_uint256(length() - 1));
  59. }
  60. }
  61. invariant consistencyKey(bytes32 key)
  62. contains(key) => (
  63. _positionOf(key) > 0 &&
  64. _positionOf(key) <= length() &&
  65. at_(require_uint256(_positionOf(key) - 1)) == key
  66. )
  67. {
  68. preserved remove(bytes32 otherKey) {
  69. requireInvariant consistencyKey(otherKey);
  70. requireInvariant atUniqueness(
  71. require_uint256(_positionOf(key) - 1),
  72. require_uint256(_positionOf(otherKey) - 1)
  73. );
  74. }
  75. }
  76. /*
  77. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  78. │ Rule: state only changes by adding or removing elements │
  79. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  80. */
  81. rule stateChange(env e, bytes32 key) {
  82. require sanity();
  83. requireInvariant consistencyKey(key);
  84. uint256 lengthBefore = length();
  85. bool containsBefore = contains(key);
  86. method f;
  87. calldataarg args;
  88. f(e, args);
  89. uint256 lengthAfter = length();
  90. bool containsAfter = contains(key);
  91. assert lengthBefore != lengthAfter => (
  92. (f.selector == sig:add(bytes32).selector && lengthAfter == require_uint256(lengthBefore + 1)) ||
  93. (f.selector == sig:remove(bytes32).selector && lengthAfter == require_uint256(lengthBefore - 1))
  94. );
  95. assert containsBefore != containsAfter => (
  96. (f.selector == sig:add(bytes32).selector && containsAfter) ||
  97. (f.selector == sig:remove(bytes32).selector && containsBefore)
  98. );
  99. }
  100. /*
  101. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  102. │ Rule: check liveness of view functions. │
  103. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  104. */
  105. rule liveness_1(bytes32 key) {
  106. requireInvariant consistencyKey(key);
  107. // contains never revert
  108. contains@withrevert(key);
  109. assert !lastReverted;
  110. }
  111. rule liveness_2(uint256 index) {
  112. requireInvariant consistencyIndex(index);
  113. // length never revert
  114. uint256 length = length@withrevert();
  115. assert !lastReverted;
  116. // at reverts iff the index is out of bound
  117. at_@withrevert(index);
  118. assert !lastReverted <=> index < length;
  119. }
  120. /*
  121. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  122. │ Rule: add key to EnumerableSet if not already contained │
  123. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  124. */
  125. rule add(bytes32 key, bytes32 otherKey) {
  126. require sanity();
  127. uint256 lengthBefore = length();
  128. bool containsBefore = contains(key);
  129. bool containsOtherBefore = contains(otherKey);
  130. bool added = add@withrevert(key);
  131. bool success = !lastReverted;
  132. assert success && contains(key),
  133. "liveness & immediate effect";
  134. assert added <=> !containsBefore,
  135. "return value: added iff not contained";
  136. assert length() == require_uint256(lengthBefore + to_mathint(added ? 1 : 0)),
  137. "effect: length increases iff added";
  138. assert added => at_(lengthBefore) == key,
  139. "effect: add at the end";
  140. assert containsOtherBefore != contains(otherKey) => (added && key == otherKey),
  141. "side effect: other keys are not affected";
  142. }
  143. /*
  144. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  145. │ Rule: remove key from EnumerableSet if already contained │
  146. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  147. */
  148. rule remove(bytes32 key, bytes32 otherKey) {
  149. requireInvariant consistencyKey(key);
  150. requireInvariant consistencyKey(otherKey);
  151. uint256 lengthBefore = length();
  152. bool containsBefore = contains(key);
  153. bool containsOtherBefore = contains(otherKey);
  154. bool removed = remove@withrevert(key);
  155. bool success = !lastReverted;
  156. assert success && !contains(key),
  157. "liveness & immediate effect";
  158. assert removed <=> containsBefore,
  159. "return value: removed iff contained";
  160. assert length() == require_uint256(lengthBefore - to_mathint(removed ? 1 : 0)),
  161. "effect: length decreases iff removed";
  162. assert containsOtherBefore != contains(otherKey) => (removed && key == otherKey),
  163. "side effect: other keys are not affected";
  164. }
  165. /*
  166. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  167. │ Rule: when adding a new key, the other keys remain in set, at the same index. │
  168. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  169. */
  170. rule addEnumerability(bytes32 key, uint256 index) {
  171. require sanity();
  172. bytes32 atBefore = at_(index);
  173. add(key);
  174. bytes32 atAfter = at_@withrevert(index);
  175. bool atAfterSuccess = !lastReverted;
  176. assert atAfterSuccess;
  177. assert atBefore == atAfter;
  178. }
  179. /*
  180. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  181. │ Rule: when removing a existing key, the other keys remain in set, at the same index (except for the last one). │
  182. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  183. */
  184. rule removeEnumerability(bytes32 key, uint256 index) {
  185. uint256 last = require_uint256(length() - 1);
  186. requireInvariant consistencyKey(key);
  187. requireInvariant consistencyIndex(index);
  188. requireInvariant consistencyIndex(last);
  189. bytes32 atBefore = at_(index);
  190. bytes32 lastBefore = at_(last);
  191. remove(key);
  192. // can't read last value (length decreased)
  193. bytes32 atAfter = at_@withrevert(index);
  194. assert lastReverted <=> index == last;
  195. // One value that is allowed to change is if previous value was removed,
  196. // in that case the last value before took its place.
  197. assert (
  198. index != last &&
  199. atBefore != atAfter
  200. ) => (
  201. atBefore == key &&
  202. atAfter == lastBefore
  203. );
  204. }