|
@@ -0,0 +1,334 @@
|
|
|
+import "helpers.spec"
|
|
|
+
|
|
|
+methods {
|
|
|
+ // library
|
|
|
+ set(bytes32,bytes32) returns (bool) envfree
|
|
|
+ remove(bytes32) returns (bool) envfree
|
|
|
+ contains(bytes32) returns (bool) envfree
|
|
|
+ length() returns (uint256) envfree
|
|
|
+ key_at(uint256) returns (bytes32) envfree
|
|
|
+ value_at(uint256) returns (bytes32) envfree
|
|
|
+ tryGet_contains(bytes32) returns (bool) envfree
|
|
|
+ tryGet_value(bytes32) returns (bytes32) envfree
|
|
|
+ get(bytes32) returns (bytes32) envfree
|
|
|
+
|
|
|
+ // FV
|
|
|
+ _indexOf(bytes32) returns (uint256) envfree
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Helpers โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+function sanity() returns bool {
|
|
|
+ return length() < max_uint256;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Invariant: the value mapping is empty for keys that are not in the EnumerableMap. โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+invariant noValueIfNotContained(bytes32 key)
|
|
|
+ !contains(key) => tryGet_value(key) == 0
|
|
|
+ {
|
|
|
+ preserved set(bytes32 otherKey, bytes32 someValue) {
|
|
|
+ require sanity();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Invariant: All indexed keys are contained โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+invariant indexedContained(uint256 index)
|
|
|
+ index < length() => contains(key_at(index))
|
|
|
+ {
|
|
|
+ preserved {
|
|
|
+ requireInvariant consistencyIndex(index);
|
|
|
+ requireInvariant consistencyIndex(to_uint256(length() - 1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Invariant: A value can only be stored at a single location โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+invariant atUniqueness(uint256 index1, uint256 index2)
|
|
|
+ index1 == index2 <=> key_at(index1) == key_at(index2)
|
|
|
+ {
|
|
|
+ preserved remove(bytes32 key) {
|
|
|
+ requireInvariant atUniqueness(index1, to_uint256(length() - 1));
|
|
|
+ requireInvariant atUniqueness(index2, to_uint256(length() - 1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Invariant: index <> value relationship is consistent โ
|
|
|
+โ โ
|
|
|
+โ Note that the two consistencyXxx invariants, put together, prove that at_ and _indexOf are inverse of one another. โ
|
|
|
+โ This proves that we have a bijection between indices (the enumerability part) and keys (the entries that are set โ
|
|
|
+โ and removed from the EnumerableMap). โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+invariant consistencyIndex(uint256 index)
|
|
|
+ index < length() => _indexOf(key_at(index)) == index + 1
|
|
|
+ {
|
|
|
+ preserved remove(bytes32 key) {
|
|
|
+ requireInvariant consistencyIndex(to_uint256(length() - 1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+invariant consistencyKey(bytes32 key)
|
|
|
+ contains(key) => (
|
|
|
+ _indexOf(key) > 0 &&
|
|
|
+ _indexOf(key) <= length() &&
|
|
|
+ key_at(to_uint256(_indexOf(key) - 1)) == key
|
|
|
+ )
|
|
|
+ {
|
|
|
+ preserved remove(bytes32 otherKey) {
|
|
|
+ requireInvariant consistencyKey(otherKey);
|
|
|
+ requireInvariant atUniqueness(
|
|
|
+ to_uint256(_indexOf(key) - 1),
|
|
|
+ to_uint256(_indexOf(otherKey) - 1)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Rule: state only changes by setting or removing elements โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+rule stateChange(env e, bytes32 key) {
|
|
|
+ require sanity();
|
|
|
+ requireInvariant consistencyKey(key);
|
|
|
+
|
|
|
+ uint256 lengthBefore = length();
|
|
|
+ bool containsBefore = contains(key);
|
|
|
+ bytes32 valueBefore = tryGet_value(key);
|
|
|
+
|
|
|
+ method f;
|
|
|
+ calldataarg args;
|
|
|
+ f(e, args);
|
|
|
+
|
|
|
+ uint256 lengthAfter = length();
|
|
|
+ bool containsAfter = contains(key);
|
|
|
+ bytes32 valueAfter = tryGet_value(key);
|
|
|
+
|
|
|
+ assert lengthBefore != lengthAfter => (
|
|
|
+ (f.selector == set(bytes32,bytes32).selector && lengthAfter == lengthBefore + 1) ||
|
|
|
+ (f.selector == remove(bytes32).selector && lengthAfter == lengthBefore - 1)
|
|
|
+ );
|
|
|
+
|
|
|
+ assert containsBefore != containsAfter => (
|
|
|
+ (f.selector == set(bytes32,bytes32).selector && containsAfter) ||
|
|
|
+ (f.selector == remove(bytes32).selector && !containsAfter)
|
|
|
+ );
|
|
|
+
|
|
|
+ assert valueBefore != valueAfter => (
|
|
|
+ (f.selector == set(bytes32,bytes32).selector && containsAfter) ||
|
|
|
+ (f.selector == remove(bytes32).selector && !containsAfter && valueAfter == 0)
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Rule: check liveness of view functions. โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+rule liveness_1(bytes32 key) {
|
|
|
+ requireInvariant consistencyKey(key);
|
|
|
+
|
|
|
+ // contains never revert
|
|
|
+ bool contains = contains@withrevert(key);
|
|
|
+ assert !lastReverted;
|
|
|
+
|
|
|
+ // tryGet never reverts (key)
|
|
|
+ tryGet_contains@withrevert(key);
|
|
|
+ assert !lastReverted;
|
|
|
+
|
|
|
+ // tryGet never reverts (value)
|
|
|
+ tryGet_value@withrevert(key);
|
|
|
+ assert !lastReverted;
|
|
|
+
|
|
|
+ // get reverts iff the key is not in the map
|
|
|
+ get@withrevert(key);
|
|
|
+ assert !lastReverted <=> contains;
|
|
|
+}
|
|
|
+
|
|
|
+rule liveness_2(uint256 index) {
|
|
|
+ requireInvariant consistencyIndex(index);
|
|
|
+
|
|
|
+ // length never revert
|
|
|
+ uint256 length = length@withrevert();
|
|
|
+ assert !lastReverted;
|
|
|
+
|
|
|
+ // key_at reverts iff the index is out of bound
|
|
|
+ key_at@withrevert(index);
|
|
|
+ assert !lastReverted <=> index < length;
|
|
|
+
|
|
|
+ // value_at reverts iff the index is out of bound
|
|
|
+ value_at@withrevert(index);
|
|
|
+ assert !lastReverted <=> index < length;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Rule: get and tryGet return the expected values. โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+rule getAndTryGet(bytes32 key) {
|
|
|
+ requireInvariant noValueIfNotContained(key);
|
|
|
+
|
|
|
+ bool contained = contains(key);
|
|
|
+ bool tryContained = tryGet_contains(key);
|
|
|
+ bytes32 tryValue = tryGet_value(key);
|
|
|
+ bytes32 value = get@withrevert(key); // revert is not contained
|
|
|
+
|
|
|
+ assert contained == tryContained;
|
|
|
+ assert contained => tryValue == value;
|
|
|
+ assert !contained => tryValue == 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Rule: set key-value in EnumerableMap โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+rule set(bytes32 key, bytes32 value, bytes32 otherKey) {
|
|
|
+ require sanity();
|
|
|
+
|
|
|
+ uint256 lengthBefore = length();
|
|
|
+ bool containsBefore = contains(key);
|
|
|
+ bool containsOtherBefore = contains(otherKey);
|
|
|
+ bytes32 otherValueBefore = tryGet_value(otherKey);
|
|
|
+
|
|
|
+ bool added = set@withrevert(key, value);
|
|
|
+ bool success = !lastReverted;
|
|
|
+
|
|
|
+ assert success && contains(key) && get(key) == value,
|
|
|
+ "liveness & immediate effect";
|
|
|
+
|
|
|
+ assert added <=> !containsBefore,
|
|
|
+ "return value: added iff not contained";
|
|
|
+
|
|
|
+ assert length() == lengthBefore + to_mathint(added ? 1 : 0),
|
|
|
+ "effect: length increases iff added";
|
|
|
+
|
|
|
+ assert added => (key_at(lengthBefore) == key && value_at(lengthBefore) == value),
|
|
|
+ "effect: add at the end";
|
|
|
+
|
|
|
+ assert containsOtherBefore != contains(otherKey) => (added && key == otherKey),
|
|
|
+ "side effect: other keys are not affected";
|
|
|
+
|
|
|
+ assert otherValueBefore != tryGet_value(otherKey) => key == otherKey,
|
|
|
+ "side effect: values attached to other keys are not affected";
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Rule: remove key from EnumerableMap โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+rule remove(bytes32 key, bytes32 otherKey) {
|
|
|
+ requireInvariant consistencyKey(key);
|
|
|
+ requireInvariant consistencyKey(otherKey);
|
|
|
+
|
|
|
+ uint256 lengthBefore = length();
|
|
|
+ bool containsBefore = contains(key);
|
|
|
+ bool containsOtherBefore = contains(otherKey);
|
|
|
+ bytes32 otherValueBefore = tryGet_value(otherKey);
|
|
|
+
|
|
|
+ bool removed = remove@withrevert(key);
|
|
|
+ bool success = !lastReverted;
|
|
|
+
|
|
|
+ assert success && !contains(key),
|
|
|
+ "liveness & immediate effect";
|
|
|
+
|
|
|
+ assert removed <=> containsBefore,
|
|
|
+ "return value: removed iff contained";
|
|
|
+
|
|
|
+ assert length() == lengthBefore - to_mathint(removed ? 1 : 0),
|
|
|
+ "effect: length decreases iff removed";
|
|
|
+
|
|
|
+ assert containsOtherBefore != contains(otherKey) => (removed && key == otherKey),
|
|
|
+ "side effect: other keys are not affected";
|
|
|
+
|
|
|
+ assert otherValueBefore != tryGet_value(otherKey) => key == otherKey,
|
|
|
+ "side effect: values attached to other keys are not affected";
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Rule: when adding a new key, the other keys remain in set, at the same index. โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+rule setEnumerability(bytes32 key, bytes32 value, uint256 index) {
|
|
|
+ require sanity();
|
|
|
+
|
|
|
+ bytes32 atKeyBefore = key_at(index);
|
|
|
+ bytes32 atValueBefore = value_at(index);
|
|
|
+
|
|
|
+ set(key, value);
|
|
|
+
|
|
|
+ bytes32 atKeyAfter = key_at@withrevert(index);
|
|
|
+ assert !lastReverted;
|
|
|
+
|
|
|
+ bytes32 atValueAfter = value_at@withrevert(index);
|
|
|
+ assert !lastReverted;
|
|
|
+
|
|
|
+ assert atKeyAfter == atKeyBefore;
|
|
|
+ assert atValueAfter != atValueBefore => (
|
|
|
+ key == atKeyBefore &&
|
|
|
+ value == atValueAfter
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Rule: when removing a existing key, the other keys remain in set, at the same index (except for the last one). โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+rule removeEnumerability(bytes32 key, uint256 index) {
|
|
|
+ uint256 last = length() - 1;
|
|
|
+
|
|
|
+ requireInvariant consistencyKey(key);
|
|
|
+ requireInvariant consistencyIndex(index);
|
|
|
+ requireInvariant consistencyIndex(last);
|
|
|
+
|
|
|
+ bytes32 atKeyBefore = key_at(index);
|
|
|
+ bytes32 atValueBefore = value_at(index);
|
|
|
+ bytes32 lastKeyBefore = key_at(last);
|
|
|
+ bytes32 lastValueBefore = value_at(last);
|
|
|
+
|
|
|
+ remove(key);
|
|
|
+
|
|
|
+ // can't read last value & keys (length decreased)
|
|
|
+ bytes32 atKeyAfter = key_at@withrevert(index);
|
|
|
+ assert lastReverted <=> index == last;
|
|
|
+
|
|
|
+ bytes32 atValueAfter = value_at@withrevert(index);
|
|
|
+ assert lastReverted <=> index == last;
|
|
|
+
|
|
|
+ // One value that is allowed to change is if previous value was removed,
|
|
|
+ // in that case the last value before took its place.
|
|
|
+ assert (
|
|
|
+ index != last &&
|
|
|
+ atKeyBefore != atKeyAfter
|
|
|
+ ) => (
|
|
|
+ atKeyBefore == key &&
|
|
|
+ atKeyAfter == lastKeyBefore
|
|
|
+ );
|
|
|
+
|
|
|
+ assert (
|
|
|
+ index != last &&
|
|
|
+ atValueBefore != atValueAfter
|
|
|
+ ) => (
|
|
|
+ atValueAfter == lastValueBefore
|
|
|
+ );
|
|
|
+}
|