ERC-6617 비트 기반 권한 표준 (Bit Based Permission)
Review · 기타 · 2023-02-27 작성
스마트 컨트랙트의 권한 하나를 숫자의 비트 하나로 표현하는 표준입니다. uint256 하나로 최대 256개 권한과 2의 256제곱 가지 역할을 정의할 수 있습니다.
왜 필요한가
스마트 컨트랙트에서 누가 무엇을 할 수 있는지 정하는 권한 관리는 지금까지 주로 두 가지 방식으로 이뤄졌습니다. 컨트랙트에 주인(owner) 한 명을 두는 방식(ERC-173)이나, 역할마다 32바이트 식별자(bytes32)를 붙여 관리하는 방식(ERC-5982)입니다. 그런데 이런 방식은 권한을 확인할 때 저장소 읽기 연산이 필요해 수수료(가스)가 상대적으로 많이 들고 여러 권한을 묶어 새 역할을 만드는 유연성도 떨어집니다. 이 표준은 권한 하나를 256비트 숫자(uint256)의 비트 하나로 표현해 값이 싼 비트 연산만으로 권한을 확인하고 조합할 수 있게 합니다. 덕분에 가스 효율이 높아지고 미리 정의하지 않은 역할도 권한의 조합으로 자유롭게 만들 수 있습니다.
구현해야 하는 것
event PermissionGranted(address indexed _grantor, uint256 indexed _permission, address indexed _user);: 권한이 부여될 때 반드시 발생하는 이벤트입니다. 부여한 사람, 부여된 권한, 받은 사용자를 기록합니다.event PermissionRevoked(address indexed _revoker, uint256 indexed _permission, address indexed _user);: 권한이 회수될 때 반드시 발생하는 이벤트입니다. 회수한 사람, 회수된 권한, 잃은 사용자를 기록합니다.function hasPermission(address _user, uint256 _requiredPermission) external view returns (bool);: 사용자에게 요구 권한이 있는지 확인합니다. 사용자의 권한이 요구 권한을 모두 포함하면 true를 돌려줍니다.function grantPermission(address _user, uint256 _permissionToAdd) external returns (bool);: 사용자에게 권한을 추가합니다.function revokePermission(address _user, uint256 _permissionToRevoke) external returns (bool);: 사용자에게서 권한을 회수합니다.event UpdatePermissionDescription(uint256 indexed _permission, string indexed _name, string indexed _description);: 권한 설명이 갱신될 때 반드시 발생하는 이벤트입니다.function getPermissionDescription(uint256 _permission) external view returns (PermissionDescription memory description);: 주어진 권한의 이름과 설명을 돌려줍니다.function setPermissionDescription(uint256 _permission, string memory _name, string memory _description) external returns (bool success);: 권한에 이름과 설명을 설정합니다. 설정되면 true를 돌려주고 UpdatePermissionDescription 이벤트를 반드시 발생시킵니다.
심층 설명
권한 표현 규칙
준수 컨트랙트는 IEIP6617 인터페이스를 반드시 구현해야 합니다. 권한은 uint256으로 표현하며 하나의 권한은 uint256의 비트 하나만 차지해야 하므로 반드시 2의 거듭제곱이어야 합니다. 각 권한은 고유해야 하고 0은 권한 없음을 뜻하는 값으로 써야 합니다. 이 규칙 덕분에 uint256 하나에 최대 256개의 권한을 담을 수 있습니다.
hasPermission의 포함 판정
hasPermission(_user, _requiredPermission)은 사용자의 권한이 요구 권한의 상위집합(superset)이면 true, 아니면 false를 돌려줍니다. 즉 요구 권한에 켜져 있는 비트가 사용자 권한에도 전부 켜져 있어야 통과합니다. 권한이 모두 uint256이므로 이 판정은 비트 연산으로 처리됩니다.
부여·회수와 이벤트 의무
grantPermission(_user, _permissionToAdd)은 사용자에게 권한을 추가하고 revokePermission(_user, _permissionToRevoke)은 권한을 회수합니다. 권한이 부여될 때는 PermissionGranted 이벤트가, 회수될 때는 PermissionRevoked 이벤트가 반드시 발생해야 합니다. 두 이벤트 모두 부여자 또는 회수자, 대상 권한, 대상 사용자를 indexed 인자로 기록합니다.
메타데이터 확장 IEIP6617Meta
권한이 숫자라서 의미를 바로 알기 어렵다는 약점을 보완하는 선택적 확장입니다. 구현이 권장(RECOMMENDED)되며 기본 권한과 주요 조합에는 이름과 설명을 정의하는 것이 좋지만(SHOULD) 가능한 모든 하위 조합마다 설명을 정의하는 것은 피해야 합니다(SHOULD NOT). PermissionDescription 구조체는 permission, name, description 필드를 가지며 setPermissionDescription은 성공 시 true를 돌려주고 UpdatePermissionDescription 이벤트를 반드시 발생시켜야 합니다.
비트 방식을 택한 이유
기존에는 단일 소유자(ERC-173)나 bytes32 역할(ERC-5982)로 접근 제어를 했지만 비트와 비트마스크 연산을 쓰면 가스 효율과 유연성이 더 좋아집니다. 권한 비트마스크에 AND 연산 한 번을 하는 비용이 LOAD 연산코드(opcode)를 여러 번 호출하는 것보다 훨씬 쌉니다. 또 256개 권한으로 2의 256제곱 가지 조합(역할)이 가능하고 모든 역할을 미리 정의할 필요 없이 이진 OR 연산으로 여러 권한을 묶어 새 역할을 만들 수 있습니다.
중요도에 따른 권한 정렬
가장 중요한 권한을 최상위 비트(most significant bit)에 배치하는 식으로 비트 순서에 권한의 중요도를 담을 수 있습니다. 권한이 모두 uint256이므로 숫자 비교만으로 권한 간 크기 비교가 쉽습니다.
아직 검토 중인 제안
이 제안의 상태는 Review입니다. 아직 확정된 표준이 아니라 검토 과정에서 내용이 바뀔 수 있습니다. 원문에는 기본 구현 예시가 참조 구현으로 제공되어 있습니다.