123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- import "helpers/helpers.spec";
- import "methods/IAccessControl.spec";
- /*
- ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Identify entrypoints: only grantRole, revokeRole and renounceRole can alter permissions │
- └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- */
- rule onlyGrantCanGrant(env e, method f, bytes32 role, address account) {
- calldataarg args;
- bool hasRoleBefore = hasRole(role, account);
- f(e, args);
- bool hasRoleAfter = hasRole(role, account);
- assert (
- !hasRoleBefore &&
- hasRoleAfter
- ) => (
- f.selector == sig:grantRole(bytes32, address).selector
- );
- assert (
- hasRoleBefore &&
- !hasRoleAfter
- ) => (
- f.selector == sig:revokeRole(bytes32, address).selector ||
- f.selector == sig:renounceRole(bytes32, address).selector
- );
- }
- /*
- ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Function correctness: grantRole only affects the specified user/role combo │
- └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- */
- rule grantRoleEffect(env e, bytes32 role) {
- require nonpayable(e);
- bytes32 otherRole;
- address account;
- address otherAccount;
- bool isCallerAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
- bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
- grantRole@withrevert(e, role, account);
- bool success = !lastReverted;
- bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
- // liveness
- assert success <=> isCallerAdmin;
- // effect
- assert success => hasRole(role, account);
- // no side effect
- assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
- }
- /*
- ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Function correctness: revokeRole only affects the specified user/role combo │
- └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- */
- rule revokeRoleEffect(env e, bytes32 role) {
- require nonpayable(e);
- bytes32 otherRole;
- address account;
- address otherAccount;
- bool isCallerAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
- bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
- revokeRole@withrevert(e, role, account);
- bool success = !lastReverted;
- bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
- // liveness
- assert success <=> isCallerAdmin;
- // effect
- assert success => !hasRole(role, account);
- // no side effect
- assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
- }
- /*
- ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Function correctness: renounceRole only affects the specified user/role combo │
- └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- */
- rule renounceRoleEffect(env e, bytes32 role) {
- require nonpayable(e);
- bytes32 otherRole;
- address account;
- address otherAccount;
- bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
- renounceRole@withrevert(e, role, account);
- bool success = !lastReverted;
- bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
- // liveness
- assert success <=> account == e.msg.sender;
- // effect
- assert success => !hasRole(role, account);
- // no side effect
- assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
- }
|