123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- import "helpers.spec"
- methods {
- hasRole(bytes32, address) returns(bool) envfree
- getRoleAdmin(bytes32) returns(bytes32) envfree
- grantRole(bytes32, address)
- revokeRole(bytes32, address)
- renounceRole(bytes32, address)
- }
- /*
- ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Identify entrypoints: only grantRole, revokeRole and renounceRole can alter permissions │
- └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- */
- rule onlyGrantCanGrant(env e, bytes32 role, address account) {
- method f; calldataarg args;
- bool hasRoleBefore = hasRole(role, account);
- f(e, args);
- bool hasRoleAfter = hasRole(role, account);
- assert (
- !hasRoleBefore &&
- hasRoleAfter
- ) => (
- f.selector == grantRole(bytes32, address).selector
- );
- assert (
- hasRoleBefore &&
- !hasRoleAfter
- ) => (
- f.selector == revokeRole(bytes32, address).selector ||
- f.selector == renounceRole(bytes32, address).selector
- );
- }
- /*
- ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Function correctness: grantRole only affects the specified user/role combo │
- └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- */
- rule grantRoleEffect(env e) {
- require nonpayable(e);
- bytes32 role1; bytes32 role2;
- address account1; address account2;
- bool isCallerAdmin = hasRole(getRoleAdmin(role2), e.msg.sender);
- bool hasRoleBefore = hasRole(role1, account1);
- grantRole@withrevert(e, role2, account2);
- assert !lastReverted <=> isCallerAdmin;
- bool hasRoleAfter = hasRole(role1, account1);
- assert (
- hasRoleBefore != hasRoleAfter
- ) => (
- hasRoleAfter == true && role1 == role2 && account1 == account2
- );
- }
- /*
- ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Function correctness: revokeRole only affects the specified user/role combo │
- └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- */
- rule revokeRoleEffect(env e) {
- require nonpayable(e);
- bytes32 role1; bytes32 role2;
- address account1; address account2;
- bool isCallerAdmin = hasRole(getRoleAdmin(role2), e.msg.sender);
- bool hasRoleBefore = hasRole(role1, account1);
- revokeRole@withrevert(e, role2, account2);
- assert !lastReverted <=> isCallerAdmin;
- bool hasRoleAfter = hasRole(role1, account1);
- assert (
- hasRoleBefore != hasRoleAfter
- ) => (
- hasRoleAfter == false && role1 == role2 && account1 == account2
- );
- }
- /*
- ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Function correctness: renounceRole only affects the specified user/role combo │
- └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- */
- rule renounceRoleEffect(env e) {
- require nonpayable(e);
- bytes32 role1; bytes32 role2;
- address account1; address account2;
- bool hasRoleBefore = hasRole(role1, account1);
- renounceRole@withrevert(e, role2, account2);
- assert !lastReverted <=> account2 == e.msg.sender;
- bool hasRoleAfter = hasRole(role1, account1);
- assert (
- hasRoleBefore != hasRoleAfter
- ) => (
- hasRoleAfter == false && role1 == role2 && account1 == account2
- );
- }
|