Files
openzeppelin-contracts/certora/specs/AccessControl.spec
2023-02-28 17:12:15 +01:00

133 lines
9.2 KiB
Ruby

methods {
hasRole(bytes32, address) returns(bool) envfree
getRoleAdmin(bytes32) returns(bytes32) envfree
grantRole(bytes32, address)
revokeRole(bytes32, address)
renounceRole(bytes32, address)
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: only grantRole can grant a role │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule onlyGrantCanGrant(env e, bytes32 role, address account) {
require !hasRole(role, account);
method f; calldataarg args;
f(e, args);
assert hasRole(role, account) => f.selector == grantRole(bytes32, address).selector;
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: only revokeRole and renounceRole can grant a role │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule onlyRevokeAndRenounceCanRevoke(env e, bytes32 role, address account) {
require hasRole(role, account);
method f; calldataarg args;
f(e, args);
assert !hasRole(role, account) => (f.selector == revokeRole(bytes32, address).selector || f.selector == renounceRole(bytes32, address).selector);
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: only an account with admin role can call grantRole │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule onlyAdminCanGrant(env e, bytes32 role, address account) {
bool isAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
grantRole@withrevert(e, role, account);
assert !lastReverted => isAdmin;
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: only an account with admin role can call revokeRole │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule onlyAdminCanRevoke(env e, bytes32 role, address account) {
bool isAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
revokeRole@withrevert(e, role, account);
assert !lastReverted => isAdmin;
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: only the affected account can revoke │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
rule onlyUserCanRenounce(env e, bytes32 role, address account) {
renounceRole@withrevert(e, role, account);
assert !lastReverted => account == e.msg.sender;
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: grantRole only affects the specified user/role combo
*/
rule grantRoleEffect(env e) {
bytes32 role1; bytes32 role2;
address account1; address account2;
bool hasRoleBefore = hasRole(role1, account1);
grantRole(e, role2, account2);
bool hasRoleAfter = hasRole(role1, account1);
assert (
hasRoleBefore != hasRoleAfter
) => (
hasRoleAfter == true && role1 == role2 && account1 == account2
);
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: revokeRole only affects the specified user/role combo
*/
rule revokeRoleEffect(env e) {
bytes32 role1; bytes32 role2;
address account1; address account2;
bool hasRoleBefore = hasRole(role1, account1);
revokeRole(e, role2, account2);
bool hasRoleAfter = hasRole(role1, account1);
assert (
hasRoleBefore != hasRoleAfter
) => (
hasRoleAfter == false && role1 == role2 && account1 == account2
);
}
/*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: renounceRole only affects the specified user/role combo
*/
rule renounceRoleEffect(env e) {
bytes32 role1; bytes32 role2;
address account1; address account2;
bool hasRoleBefore = hasRole(role1, account1);
renounceRole(e, role2, account2);
bool hasRoleAfter = hasRole(role1, account1);
assert (
hasRoleBefore != hasRoleAfter
) => (
hasRoleAfter == false && role1 == role2 && account1 == account2
);
}