123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- methods {
- getTimestamp(bytes32) returns(uint256) envfree
- _DONE_TIMESTAMP() returns(uint256) envfree
- PROPOSER_ROLE() returns(bytes32) envfree
- _minDelay() returns(uint256) envfree
- getMinDelay() returns(uint256) envfree
- hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) returns(bytes32) envfree
- isOperation(bytes32) returns(bool) envfree
- isOperationPending(bytes32) returns(bool) envfree
- isOperationDone(bytes32) returns(bool) envfree
- isOperationReady(bytes32) returns(bool)
- cancel(bytes32)
- schedule(address, uint256, bytes, bytes32, bytes32, uint256)
- execute(address, uint256, bytes, bytes32, bytes32)
- scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256)
- executeBatch(address[], uint256[], bytes[], bytes32, bytes32)
- _checkRole(bytes32) => DISPATCHER(true)
- }
- ////////////////////////////////////////////////////////////////////////////
- // Functions //
- ////////////////////////////////////////////////////////////////////////////
- function hashIdCorrelation(bytes32 id, address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt){
- require data.length < 32;
- require hashOperation(target, value, data, predecessor, salt) == id;
- }
- ////////////////////////////////////////////////////////////////////////////
- // Invariants //
- ////////////////////////////////////////////////////////////////////////////
- // STATUS - verified
- // `isOperation()` correctness check
- invariant operationCheck(bytes32 id)
- getTimestamp(id) > 0 <=> isOperation(id)
- filtered { f -> !f.isView }
- // STATUS - verified
- // `isOperationPending()` correctness check
- invariant pendingCheck(bytes32 id)
- getTimestamp(id) > _DONE_TIMESTAMP() <=> isOperationPending(id)
- filtered { f -> !f.isView }
- // STATUS - verified
- // `isOperationReady()` correctness check
- invariant readyCheck(env e, bytes32 id)
- (e.block.timestamp >= getTimestamp(id) && getTimestamp(id) > 1) <=> isOperationReady(e, id)
- filtered { f -> !f.isView }
- // STATUS - verified
- // `isOperationDone()` correctness check
- invariant doneCheck(bytes32 id)
- getTimestamp(id) == _DONE_TIMESTAMP() <=> isOperationDone(id)
- filtered { f -> !f.isView }
- ////////////////////////////////////////////////////////////////////////////
- // Rules //
- ////////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////
- // STATE TRANSITIONS
- /////////////////////////////////////////////////////////////
- // STATUS - verified
- // Possible transitions: form `!isOperation()` to `!isOperation()` or `isOperationPending()` only
- rule unsetPendingTransitionGeneral(method f, env e){
- bytes32 id;
- require !isOperation(id);
- require e.block.timestamp > 1;
- calldataarg args;
- f(e, args);
- assert isOperationPending(id) || !isOperation(id);
- }
- // STATUS - verified
- // Possible transitions: form `!isOperation()` to `isOperationPending()` via `schedule()` and `scheduleBatch()` only
- rule unsetPendingTransitionMethods(method f, env e){
- bytes32 id;
- require !isOperation(id);
- calldataarg args;
- f(e, args);
- assert isOperationPending(id) => (f.selector == schedule(address, uint256, bytes, bytes32, bytes32, uint256).selector
- || f.selector == scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256).selector), "Why do we need to follow the schedule?";
- }
- // STATUS - verified
- // Possible transitions: form `ready()` to `isOperationDone()` via `execute()` and `executeBatch()` only
- rule readyDoneTransition(method f, env e){
- bytes32 id;
- require isOperationReady(e, id);
- calldataarg args;
- f(e, args);
- assert isOperationDone(id) => f.selector == execute(address, uint256, bytes, bytes32, bytes32).selector
- || f.selector == executeBatch(address[], uint256[], bytes[], bytes32, bytes32).selector , "It's not isOperationDone yet!";
- }
- // STATUS - verified
- // isOperationPending() -> cancelled() via cancel() only
- rule pendingCancelledTransition(method f, env e){
- bytes32 id;
- require isOperationPending(id);
- calldataarg args;
- f(e, args);
- assert !isOperation(id) => f.selector == cancel(bytes32).selector, "How you dare to cancel me?";
- }
- // STATUS - verified
- // isOperationDone() -> nowhere
- rule doneToNothingTransition(method f, env e){
- bytes32 id;
- require isOperationDone(id);
- calldataarg args;
- f(e, args);
- assert isOperationDone(id), "Did you find a way to escape? There is no way! HA-HA-HA";
- }
- /////////////////////////////////////////////////////////////
- // THE REST
- /////////////////////////////////////////////////////////////
- // STATUS - verified
- // only TimelockController contract can change minDelay
- rule minDelayOnlyChange(method f, env e){
- uint256 delayBefore = _minDelay();
- calldataarg args;
- f(e, args);
- uint256 delayAfter = _minDelay();
- assert delayBefore != delayAfter => e.msg.sender == currentContract, "You cannot change your destiny! Only I can!";
- }
- // STATUS - verified
- // scheduled operation timestamp == block.timestamp + delay (kind of unit test)
- rule scheduleCheck(method f, env e){
- bytes32 id;
- address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay;
- hashIdCorrelation(id, target, value, data, predecessor, salt);
- schedule(e, target, value, data, predecessor, salt, delay);
- assert getTimestamp(id) == to_uint256(e.block.timestamp + delay), "Time doesn't obey to mortal souls";
- }
- // STATUS - verified
- // Cannot call `execute()` on a isOperationPending (not ready) operation
- rule cannotCallExecute(method f, env e){
- address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt;
- bytes32 id;
- hashIdCorrelation(id, target, value, data, predecessor, salt);
- require isOperationPending(id) && !isOperationReady(e, id);
- execute@withrevert(e, target, value, data, predecessor, salt);
- assert lastReverted, "you go against execution nature";
- }
- // STATUS - verified
- // Cannot call `execute()` on a !isOperation operation
- rule executeRevertsFromUnset(method f, env e, env e2){
- address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt;
- bytes32 id;
- hashIdCorrelation(id, target, value, data, predecessor, salt);
- require !isOperation(id);
- execute@withrevert(e, target, value, data, predecessor, salt);
- assert lastReverted, "you go against execution nature";
- }
- // STATUS - verified
- // Execute reverts => state returns to isOperationPending
- rule executeRevertsEffectCheck(method f, env e){
- address target; uint256 value; bytes data; bytes32 predecessor; bytes32 salt;
- bytes32 id;
- hashIdCorrelation(id, target, value, data, predecessor, salt);
- require isOperationPending(id) && !isOperationReady(e, id);
- execute@withrevert(e, target, value, data, predecessor, salt);
- bool reverted = lastReverted;
- assert lastReverted => isOperationPending(id) && !isOperationReady(e, id), "you go against execution nature";
- }
- // STATUS - verified
- // Canceled operations cannot be executed → can’t move from canceled to isOperationDone
- rule cancelledNotExecuted(method f, env e){
- bytes32 id;
- require !isOperation(id);
- require e.block.timestamp > 1;
- calldataarg args;
- f(e, args);
- assert !isOperationDone(id), "The ship is not a creature of the air";
- }
- // STATUS - verified
- // Only proposers can schedule
- rule onlyProposer(method f, env e) filtered { f -> f.selector == schedule(address, uint256, bytes, bytes32, bytes32, uint256).selector
- || f.selector == scheduleBatch(address[], uint256[], bytes[], bytes32, bytes32, uint256).selector } {
- bytes32 id;
- bytes32 role;
- address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay;
- _checkRole@withrevert(e, PROPOSER_ROLE());
- bool isCheckRoleReverted = lastReverted;
- calldataarg args;
- f@withrevert(e, args);
- bool isScheduleReverted = lastReverted;
- assert isCheckRoleReverted => isScheduleReverted, "Enemy was detected";
- }
- // STATUS - verified
- // if `ready` then has waited minimum period after isOperationPending()
- rule cooldown(method f, env e, env e2){
- bytes32 id;
- address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay;
- uint256 minDelay = getMinDelay();
- hashIdCorrelation(id, target, value, data, predecessor, salt);
- schedule(e, target, value, data, predecessor, salt, delay);
- calldataarg args;
- f(e, args);
- assert isOperationReady(e2, id) => (e2.block.timestamp - e.block.timestamp >= minDelay), "No rush! When I'm ready, I'm ready";
- }
- // STATUS - verified
- // `schedule()` should change only one id's timestamp
- rule scheduleChange(env e){
- bytes32 id; bytes32 otherId;
- address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay;
- uint256 otherIdTimestampBefore = getTimestamp(otherId);
- hashIdCorrelation(id, target, value, data, predecessor, salt);
- schedule(e, target, value, data, predecessor, salt, delay);
- assert id != otherId => otherIdTimestampBefore == getTimestamp(otherId), "Master of puppets, I'm pulling your strings";
- }
- // STATUS - verified
- // `execute()` should change only one id's timestamp
- rule executeChange(env e){
- bytes32 id; bytes32 otherId;
- address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt;
- uint256 otherIdTimestampBefore = getTimestamp(otherId);
- hashIdCorrelation(id, target, value, data, predecessor, salt);
- execute(e, target, value, data, predecessor, salt);
- assert id != otherId => otherIdTimestampBefore == getTimestamp(otherId), "Master of puppets, I'm pulling your strings";
- }
- // STATUS - verified
- // `cancel()` should change only one id's timestamp
- rule cancelChange(env e){
- bytes32 id; bytes32 otherId;
- uint256 otherIdTimestampBefore = getTimestamp(otherId);
- cancel(e, id);
- assert id != otherId => otherIdTimestampBefore == getTimestamp(otherId), "Master of puppets, I'm pulling your strings";
- }
|