123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- import "helpers/helpers.spec";
- methods {
- // library
- function set(bytes32,bytes32) external returns (bool) envfree;
- function remove(bytes32) external returns (bool) envfree;
- function contains(bytes32) external returns (bool) envfree;
- function length() external returns (uint256) envfree;
- function key_at(uint256) external returns (bytes32) envfree;
- function value_at(uint256) external returns (bytes32) envfree;
- function tryGet_contains(bytes32) external returns (bool) envfree;
- function tryGet_value(bytes32) external returns (bytes32) envfree;
- function get(bytes32) external returns (bytes32) envfree;
- // FV
- function _positionOf(bytes32) external returns (uint256) envfree;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Helpers โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- definition sanity() returns bool =
- 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) == to_bytes32(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(require_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, require_uint256(length() - 1));
- requireInvariant atUniqueness(index2, require_uint256(length() - 1));
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: index <> value relationship is consistent โ
- โ โ
- โ Note that the two consistencyXxx invariants, put together, prove that at_ and _positionOf 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() => to_mathint(_positionOf(key_at(index))) == index + 1
- {
- preserved remove(bytes32 key) {
- requireInvariant consistencyIndex(require_uint256(length() - 1));
- }
- }
- invariant consistencyKey(bytes32 key)
- contains(key) => (
- _positionOf(key) > 0 &&
- _positionOf(key) <= length() &&
- key_at(require_uint256(_positionOf(key) - 1)) == key
- )
- {
- preserved remove(bytes32 otherKey) {
- requireInvariant consistencyKey(otherKey);
- requireInvariant atUniqueness(
- require_uint256(_positionOf(key) - 1),
- require_uint256(_positionOf(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 == sig:set(bytes32,bytes32).selector && to_mathint(lengthAfter) == lengthBefore + 1) ||
- (f.selector == sig:remove(bytes32).selector && to_mathint(lengthAfter) == lengthBefore - 1)
- );
- assert containsBefore != containsAfter => (
- (f.selector == sig:set(bytes32,bytes32).selector && containsAfter) ||
- (f.selector == sig:remove(bytes32).selector && !containsAfter)
- );
- assert valueBefore != valueAfter => (
- (f.selector == sig:set(bytes32,bytes32).selector && containsAfter) ||
- (f.selector == sig:remove(bytes32).selector && !containsAfter && valueAfter == to_bytes32(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 == to_bytes32(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 to_mathint(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 to_mathint(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 = require_uint256(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
- );
- }
|