forked from DecentralizedClimateFoundation/DCIPs
153 lines
5.1 KiB
Markdown
153 lines
5.1 KiB
Markdown
|
---
|
||
|
eip: 6617
|
||
|
title: Bit Based Permission
|
||
|
description: A permission and role system based on bits
|
||
|
author: Chiro (@chiro-hiro), Victor Dusart (@vdusart)
|
||
|
discussions-to: https://ethereum-magicians.org/t/bit-based-permission/13065
|
||
|
status: Draft
|
||
|
type: Standards Track
|
||
|
category: ERC
|
||
|
created: 2023-02-27
|
||
|
---
|
||
|
|
||
|
## Abstract
|
||
|
|
||
|
This EIP offers a standard for building a bit-based permission and role system. Each permission is represented by a single bit. By using an `uint256`, up to $256$ permissions and $2^{256}$ roles can be defined. We are able to specify the importance of each permission based on the order of the bits.
|
||
|
|
||
|
|
||
|
## Specification
|
||
|
|
||
|
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
|
||
|
|
||
|
*Note* The following specifications use syntax from Solidity `0.8.7` (or above)
|
||
|
|
||
|
- Permission and role MUST be defined as an `uint256`
|
||
|
- Permission MUST be defined as a power of two
|
||
|
- Permission MUST be unique
|
||
|
- `0` MUST be used for none permission
|
||
|
|
||
|
## Rationale
|
||
|
|
||
|
Currently permission and access control is performed using a single owner ([ERC-173](./eip-173.md)) or with `bytes32` roles ([ERC-5982](./eip-5982.md)).
|
||
|
However, using bitwise and bitmask operations allows for greater gas-efficiency and flexibility.
|
||
|
|
||
|
### Gas cost efficiency
|
||
|
|
||
|
Bitwise operations are very cheap and fast. For example, doing an `AND` bitwise operation on a permission bitmask is significantly cheaper than calling any number of `LOAD` opcodes.
|
||
|
|
||
|
### Flexibility
|
||
|
|
||
|
With the 256 bits of the `uint256`, we can create up to 256 different permissions which leads to $2^{256}$ unique combinations (a.k.a. roles).
|
||
|
*(A role is a combination of multiple permissions).* Not all roles have to be predefined.
|
||
|
|
||
|
Since permissions are defined as unsigned integers, we can use the binary OR operator to create new role based on multiple permissions.
|
||
|
|
||
|
### Ordering permissions by importance
|
||
|
|
||
|
We can use the most significant bit to represent the most important permission, the comparison between permissions can then be done easily since they all are `uint256`s.
|
||
|
|
||
|
## Test Cases
|
||
|
|
||
|
```solidity
|
||
|
pragma solidity ^0.8.7;
|
||
|
|
||
|
import "EIP6617.sol";
|
||
|
|
||
|
contract Test {
|
||
|
using EIP6617 for uint256;
|
||
|
|
||
|
uint256 public constant PERMISSION_NONE = 0;
|
||
|
uint256 public constant PERMISSION_READ = 1; // 2⁰
|
||
|
uint256 public constant PERMISSION_WRITE = 2; // 2¹
|
||
|
uint256 public constant PERMISSION_EXECUTE = 4; // 2²
|
||
|
|
||
|
// Role operator = 1 | 2 = 3
|
||
|
uint256 public constant ROLE_OPERATOR = PERMISSION_READ | PERMISSION_WRITE;
|
||
|
|
||
|
// Role admin = 1 | 2 | 4 = 7
|
||
|
uint256 public constant ROLE_ADMIN = PERMISSION_READ | PERMISSION_WRITE | PERMISSION_EXECUTE;
|
||
|
|
||
|
function testPermissions() external pure returns (uint256) {
|
||
|
uint256 userPermission;
|
||
|
|
||
|
// adding read permission
|
||
|
userPermission = userPermission.permissionGrant(PERMISSION_READ);
|
||
|
|
||
|
// adding admin role
|
||
|
userPermission = userPermission.permissionGrant(ROLE_ADMIN);
|
||
|
|
||
|
// removing execute permission
|
||
|
userPermission = userPermission.permissionRevoke(PERMISSION_EXECUTE);
|
||
|
|
||
|
// Checking permission
|
||
|
if (userPermission.permissionCheck(ROLE_ADMIN)) {
|
||
|
// Only admin can access this part
|
||
|
}
|
||
|
|
||
|
return userPermission;
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Reference Implementation
|
||
|
|
||
|
```solidity
|
||
|
pragma solidity ^0.8.7;
|
||
|
|
||
|
/**
|
||
|
@title EIP-6617 Bit Based Permission
|
||
|
@dev See https://eips.ethereum.org/EIPS/eip-6617
|
||
|
*/
|
||
|
library EIP6617 {
|
||
|
/**
|
||
|
@notice Check if _permission is a superset of _requiredPermission
|
||
|
@param _permission The given permission
|
||
|
@param _requiredPermission The required permission
|
||
|
@return True if the _permission is a superset of the _requiredPermission else False
|
||
|
*/
|
||
|
function permissionCheck(uint256 _permission, uint256 _requiredPermission)
|
||
|
internal
|
||
|
pure
|
||
|
returns (bool)
|
||
|
{
|
||
|
return _permission & _requiredPermission == _requiredPermission;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@notice Add permission
|
||
|
@param _permission The given permission
|
||
|
@param _permissionToAdd The permission that will be added
|
||
|
@return The new permission with the _permissionToAdd
|
||
|
*/
|
||
|
function permissionGrant(uint256 _permission, uint256 _permissionToAdd)
|
||
|
internal
|
||
|
pure
|
||
|
returns (uint256)
|
||
|
{
|
||
|
return _permission | _permissionToAdd;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@notice Remove permission
|
||
|
@param _permission The given permission
|
||
|
@param _permissionToRemove The permission that will be removed
|
||
|
@return The new permission without the _permissionToRemove
|
||
|
*/
|
||
|
function permissionRevoke(uint256 _permission, uint256 _permissionToRemove)
|
||
|
internal
|
||
|
pure
|
||
|
returns (uint256)
|
||
|
{
|
||
|
return (_permission | _permissionToRemove) ^ _permissionToRemove;
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Security Considerations
|
||
|
|
||
|
Need more discussion.
|
||
|
|
||
|
## Copyright
|
||
|
|
||
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|