mirror of openzeppelin-contracts
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openzeppelin-contracts/certora/specs/AccessControl.spec

115 lines
5.7 KiB

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
);
}