Access control with OpenZeppelin in Blockchain / Solidity - Time & Space Complexity
Start learning this pattern below
Jump into concepts and practice - no test required
When using OpenZeppelin's access control, we want to know how the cost of checking permissions grows as more roles or users are added.
We ask: How does the time to verify access change with input size?
Analyze the time complexity of the following Solidity code using OpenZeppelin's AccessControl.
contract MyContract is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
function restrictedAction() public {
require(hasRole(ADMIN_ROLE, msg.sender), "Access denied");
// action code here
}
}
This code checks if the caller has the ADMIN_ROLE before allowing an action.
Look at what repeats when checking access.
- Primary operation: Checking if an address has a role in a mapping.
- How many times: Once per call to
restrictedAction.
The check looks up a role in a mapping, which stays fast even if many users or roles exist.
| Input Size (number of roles/users) | Approx. Operations |
|---|---|
| 10 | 1 lookup |
| 100 | 1 lookup |
| 1000 | 1 lookup |
Pattern observation: The time to check access stays about the same no matter how many roles or users there are.
Time Complexity: O(1)
This means checking access takes the same amount of time regardless of how many roles or users exist.
[X] Wrong: "Checking access gets slower as more users or roles are added."
[OK] Correct: The role check uses a direct mapping lookup, which is constant time no matter the size.
Understanding how access control checks scale helps you design secure and efficient smart contracts, a key skill in blockchain development.
What if the contract checked multiple roles in a loop? How would the time complexity change?
Practice
onlyRole modifier in OpenZeppelin's Access Control?Solution
Step 1: Understand the purpose of
TheonlyRoleonlyRolemodifier is used to limit access to functions so only users with a certain role can execute them.Step 2: Analyze the options
To restrict function access to accounts with a specific role correctly states that it restricts function access to accounts with a specific role. Other options describe unrelated behaviors.Final Answer:
To restrict function access to accounts with a specific role -> Option AQuick Check:
Access control = restrict by role [OK]
- Thinking onlyRole assigns roles automatically
- Believing onlyRole allows open access
- Confusing onlyRole with event logging
Solution
Step 1: Recall role declaration syntax
OpenZeppelin usesbytes32constants withkeccak256hash of a string to define roles.Step 2: Check each option
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); matches the correct pattern. Options B, C, and D use wrong types or formats.Final Answer:
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); -> Option DQuick Check:
Role constants use bytes32 + keccak256 [OK]
- Using string instead of bytes32 for roles
- Assigning numeric or address types to roles
- Forgetting to use keccak256 hash
secureFunction()?
contract MyContract is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
constructor() {
_grantRole(ADMIN_ROLE, msg.sender);
}
function secureFunction() public onlyRole(ADMIN_ROLE) {
// critical logic
}
}Solution
Step 1: Understand the
TheonlyRolemodifier behavioronlyRole(ADMIN_ROLE)modifier restricts access to accounts with ADMIN_ROLE. If the caller lacks this role, the call reverts.Step 2: Analyze the scenario
The constructor grants ADMIN_ROLE only tomsg.senderat deployment. Any other account callingsecureFunctionwill trigger a revert due to missing role.Final Answer:
The call reverts with an access control error -> Option BQuick Check:
Missing role causes revert [OK]
- Assuming function runs without role
- Thinking warnings are emitted instead of revert
- Confusing role with ownership
contract MyContract is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
constructor() {
_grantRole(ADMIN_ROLE, msg.sender);
}
function restricted() public onlyRole(ADMIN_ROLE) {
// restricted logic
}
function grantAdmin(address user) public {
_grantRole(ADMIN_ROLE, user);
}
}Solution
Step 1: Review access control on
ThegrantAdmingrantAdminfunction calls_grantRolebut has no modifier restricting who can call it.Step 2: Identify security risk
Without access control, anyone can callgrantAdminand assign ADMIN_ROLE to themselves or others, breaking security.Final Answer:
ThegrantAdminfunction lacks access control and can be called by anyone -> Option AQuick Check:
Grant functions need access control [OK]
- Ignoring missing access control on grant functions
- Thinking _grantRole is protected like grantRole
- Confusing role declaration syntax
Solution
Step 1: Understand role assignment control
To restrict who can assign MINTER_ROLE, use AccessControl's DEFAULT_ADMIN_ROLE for admin rights and protect assignment functions withonlyRole(DEFAULT_ADMIN_ROLE).Step 2: Connect owner with DEFAULT_ADMIN_ROLE
Grant DEFAULT_ADMIN_ROLE to the contract owner so only they can assign MINTER_ROLE to others.Final Answer:
Use AccessControl with MINTER_ROLE and add onlyRole(DEFAULT_ADMIN_ROLE) modifier to the role assignment function, granting DEFAULT_ADMIN_ROLE to the owner -> Option CQuick Check:
Admin role controls role assignment [OK]
- Allowing anyone to assign roles
- Using Ownable without roles for minting
- Assigning roles to everyone by default
