[ERC6366] 256ビット数値による効率的かつ柔軟な権限管理の仕組みを理解しよう！

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回は、256ビットの数値でアクセス権を管理し、効率的かつ柔軟な権限管理の仕組みを提案している規格である**ERC6366**についてまとめていきます！

:::note info
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-6366

:::note warn
**6366**は現在（2024年1月9日）では「**Review**」段階です。
:::

他にも様々なEIPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

# 概要

このEIP（イーサリアム改善提案）は、アクセスコントロールリスト（ACL）を代替する新しい認証付与とセキュリティ強化の方法を提案しています。
このシステムでは、イーサリアムのエコシステム内の特定のアドレスの権限を管理するために、256ビットの数値（`uint256`）を使用します。
**ERC6617**で説明されているように、この`uint256`内の各ビットが異なるアクセス権を表しています。

この方法の利点は、ビットごとの演算子とビットマスクを使ってアクセス権を決定することで、従来の文字列比較や**keccak256**ハッシュ比較よりもはるかに効率的で柔軟な権限管理が可能になる点です。
例えば、`uint256`の特定のビットが`1`の場合、そのビットは特定のアドレスに対して特定のアクションを実行する権限があることを示します。
逆に、ビットが`0`の場合はそのアクションを実行する権限がないことを意味します。

このシステムでは、たとえば`checkPermission`という関数を使用して、特定のアドレスが特定のアクションを実行するための権限を持っているかどうかを効率的に確認できます。
ビットマスクを使用して特定のビットが`1`かどうかをチェックし、その結果に基づいてアクセス権を決定します。
このアプローチは、セキュリティを維持しながら、権限管理をより柔軟かつ効率的に行うことを可能にします。

# 動機

スマートコントラクトでは、「**オーナー（`Owner`）**」、「**オペレーター（`Operator`）**」、「**マネージャー（`Manager`）**」、「**バリデーター（`Validator`）**」といった特定のロールがよく用いられます。
これらのロールは、コントラクトを運営・管理するために許可されたアドレスに割り当てられています。
しかし、これらの権限が一つのコントラクトで一元管理されていないため、監査やメンテナンスが困難になっています。

この問題に対処するために、権限とロールを、特定のエコシステム内の関連アカウントの権限トークンバランスによって表す方法が提案されています。
このアプローチでは、異なるエコシステム間での相互作用がより簡単になります。

例えば、あるエコシステム内で「**オーナー**」ロールを持つアカウントは、そのロールに応じた権限トークンのバランスを持つことになります。
これにより、異なるエコシステム間でのロールや権限の移行や調整が容易になり、スマートコントラクトの運用が効率的かつ柔軟に行えるようになります。

さらに、このシステムを採用することで、権限の管理や監査が簡素化されます。
アカウントのトークンバランスを確認するだけで、そのアカウントがどのようなロールや権限を持っているかが明確になります。
これにより、システム全体の透明性が向上し、スマートコントラクトのセキュリティと効率性が高まります。また、異なるエコシステム間での連携もスムーズに行えるようになります。

# 仕様

以下の仕様は、Solidity `0.8.7`（またはそれ以上）の構文を使用しています。

## コアインターフェース

この規格に準拠するコントラクトは、`IEIP6366Core`を実装する必要があります。

**ERC6617**を使用してパーミッションのセット間の関係をチェックできるように、各パーミッションを2のべき乗として定義することが推奨されます。

```javascript
interface IEIP6366Core {
  /**
   * MUST trigger when `_permission` are transferred, including `zero` permission transfers.
   * @param _from           Permission owner
   * @param _to             Permission receiver
   * @param _permission     Transferred subset permission of permission owner
   */
  event Transfer(address indexed _from, address indexed _to, uint256 indexed _permission);

  /**
   * MUST trigger on any successful call to `approve(address _delegatee, uint256 _permission)`.
   * @param _owner          Permission owner
   * @param _delegatee      Delegatee
   * @param _permission     Approved subset permission of permission owner
   */
  event Approval(address indexed _owner, address indexed _delegatee, uint256 indexed _permission);

  /**
   * Transfers a subset `_permission` of permission to address `_to`.
   * The function SHOULD revert if the message caller’s account permission does not have the subset
   * of the transferring permissions. The function SHOULD revert if any of transferring permissions are
   * existing on target `_to` address.
   * @param _to             Permission receiver
   * @param _permission     Subset permission of permission owner
   */
  function transfer(address _to, uint256 _permission) external returns (bool success);

  /**
   * Allows `_delegatee` to act for the permission owner's behalf, up to the `_permission`.
   * If this function is called again it overwrites the current granted with `_permission`.
   * `approve()` method SHOULD `revert` if granting `_permission` permission is not
   * a subset of all available permissions of permission owner.
   * @param _delegatee      Delegatee
   * @param _permission     Subset permission of permission owner
   */
  function approve(address _delegatee, uint256 _permission) external returns (bool success);

  /**
   * Returns the permissions of the given `_owner` address.
   */
  function permissionOf(address _owner) external view returns (uint256 permission);

  /**
   * Returns `true` if `_required` is a subset of `_permission` otherwise return `false`.
   * @param _permission     Checking permission set
   * @param _required       Required set of permission
   */
  function permissionRequire(uint256 _permission, uint256 _required) external view returns (bool isPermissioned);

  /**
   * Returns `true` if `_required` permission is a subset of `_actor`'s permissions or a subset of his delegated
   * permission granted by the `_owner`.
   * @param _owner          Permission owner
   * @param _actor          Actor who acts on behalf of the owner
   * @param _required       Required set of permission
   */
  function hasPermission(address _owner, address _actor, uint256 _required) external view returns (bool isPermissioned);

  /**
   * Returns the subset permission of the `_owner` address were granted to `_delegatee` address.
   * @param _owner          Permission owner
   * @param _delegatee      Delegatee
   */
  function delegated(address _owner, address _delegatee) external view returns (uint256 permission);
}
```

---

### Transfer

```javascript
event Transfer(address indexed _from, address indexed _to, uint256 indexed _permission);
```

**概要**
権限が転送されるときに発行されるイベント。

**詳細**
このイベントは、権限の所有者から別のアドレスへ権限が転送される時に発行されます。
ゼロ権限の転送も、権限の変更と見なされるため、このイベントによって記録されます。
これにより、権限の動きを追跡しやすくなります。

**パラメータ**
- `_from`
    - 権限の所有者のアドレス。
- `_to`
    - 権限を受け取るアドレス。
- `_permission`
    - 転送される権限のサブセットを表す`uint256`値。

---

### Approval

```javascript
event Approval(address indexed _owner, address indexed _delegatee, uint256 indexed _permission);
```

**概要**
`approve`関数が成功した時に発行されるイベント。

**詳細**
このイベントは、権限の所有者が別のアドレス（委任者）に特定の権限を付与する時に発行されます。
この関数の呼び出しが成功すると、権限の所有者が委任者に与えた権限のサブセットに関する情報がこのイベントを通じて公開されます。

**パラメータ**
- `_owner`
    - 権限の所有者のアドレス。
- `_delegatee`
    - 権限を付与された委任者のアドレス。
- `_permission`
    - 承認された権限のサブセットを表す`uint256`値。

---

### `transfer`

```javascript
function transfer(address _to, uint256 _permission) external returns (bool success);
```

**概要**
権限のサブセットを特定のアドレスに転送する関数。

**詳細**
この関数は、呼び出し元のアカウントが転送する権限のサブセットを持っていない場合、または転送する権限がターゲットのアドレスに既に存在する場合に`revert`するべきです。
これにより、権限の不正な移動を防ぐことができます。

**引数**
- `_to`
    - 権限を受け取るアドレス。
- `_permission`
    - 転送される権限のサブセットを表す`uint256`値。

**戻り値**
- `success`
    - 転送が成功したかどうかを示す`bool`値。

---

### `approve`

```javascript
function approve(address _delegatee, uint256 _permission) external returns (bool success);
```

**概要**
指定された委任者に特定の権限を付与する関数。

**詳細**
この関数は、権限の所有者が委任者に特定の権限を許可するために使用されます。
もし同じ委任者に対して再度呼び出された場合は、現在の許可を新しい`_permission`で上書きします。
許可される`_permission`が権限所有者の全ての利用可能な権限のサブセットでない場合、このメソッドはリバートすべきです。

**引数**
- `_delegatee`
    - 権限を付与される委任者のアドレス。
- `_permission`
    - 承認される権限のサブセットを表す`uint256`値。

**戻り値**
- `success`
    - 承認が成功したかどうかを示す`bool`値。

---

### `permissionOf`

```javascript
function permissionOf(address _owner) external view returns (uint256 permission);
```

**概要**
指定されたアドレスの権限を返す関数。

**詳細**
この関数は、指定されたアドレス（`_owner`）が持っている権限のセットを返します。
これにより、権限の所有状況を簡単に確認することができます。

**引数**
- `_owner`
    - 権限を確認するアドレス。

**戻り値**
- `permission`
    - 指定されたアドレスの権限を表す`uint256`値。

---

### `permissionRequire`

```javascript
function permissionRequire(uint256 _permission, uint256 _required) external view returns (bool isPermissioned);
```

**概要**
指定された権限が必要な権限のサブセットか確認する関数。

**詳細**
この関数は、`_required`が`_permission`のサブセットであるかどうかを判断します。
これにより、特定の権限が他の権限の範囲内にあるかどうかを簡単に確認することができます。

**引数**
- `_permission`
    - 確認する権限セット。
- `_required`
    - 必要とされる権限セット。

**戻り値**
- `isPermissioned`
    - `_required`が`_permission`のサブセットであるかどうかを示す`bool`値。

---

### `hasPermission`

```javascript
function hasPermission(address _owner, address _actor, uint256 _required) external view returns (bool isPermissioned);
```

**概要**
`

指定されたアクターが必要な権限を持っているか確認する関数。

**詳細**
この関数は、`_required`権限が`_actor`の権限のサブセットであるか、または`_owner`によって委任された権限のサブセットであるかどうかを判断します。
これにより、特定のアクターが必要な権限を持っているかどうかを簡単に確認することができます。

**引数**
- `_owner`
    - 権限の所有者のアドレス。
- `_actor`
    - 代理行為をするアクターのアドレス。
- `_required`
    - 必要とされる権限セット。

**戻り値**
- `isPermissioned`
    - 指定されたアクターが必要な権限を持っているかどうかを示す`bool`値。

---

### `delegated`

```javascript
function delegated(address _owner, address _delegatee) external view returns (uint256 permission);
```

**概要**
指定されたアドレスに委任された権限を返す関数。

**詳細**
この関数は、`_owner`アドレスが`_delegatee`アドレスに委任した権限のサブセットを返します。
これにより、特定の委任関係の権限状況を確認することができます。

**引数**
- `_owner`
    - 権限の所有者のアドレス。
- `_delegatee`
    - 権限を委任されたアドレス。

**戻り値**
- `permission`
    - 委任された権限を表す`uint256`値。

---

## メタデータインターフェース

`IEIP6617Meta`は、スマートコントラクトにおけるオプショナルなメタデータインターフェースです。
これは、特定の標準に準拠したコントラクトが実装することが推奨される機能拡張です。

このインターフェースの主な目的は、コントラクトの基本的な権限とその主な組み合わせに関する説明を提供することです。
これにより、コントラクトが持つ基本権限と、それらを組み合わせることによって形成される主要な権限のセットについての明確な情報が利用者に提供されます。
このような情報は、コントラクトの機能と権限の構造をより簡単に理解するのに役立ちます。

一方で、`IEIP6617Meta`では、すべての可能な権限のサブコンビネーション（部分組み合わせ）についての説明を定義することは推奨されていません。
全ての権限の組み合わせについて個別に説明することは、非効率的であり、実際に必要とされる情報の提供が困難になるためです。

このメタデータインターフェースを通じて、コントラクトの利用者は、コントラクトの基本的な権限とその主な組み合わせについての透明な情報を得ることができます。
これは、コントラクトの機能と権限の理解を深め、より効果的な使用を促進することを目指しています。

## エラーインターフェース

以下に定義する`IEIP6366Error`はオプション機能です。

```javascript
interface IEIP6366Error {
  /**
   * The owner or actor does not have the required permission
   */
  error AccessDenied(address _owner, address _actor, uint256 _permission);

  /**
   * Conflict between permission set
   */
  error DuplicatedPermission(uint256 _permission);

  /**
   * Data out of range
   */
  error OutOfRange();
}
```

---

### AccessDenied

```javascript
error AccessDenied(address _owner, address _actor, uint256 _permission);
```

**概要**
所有者またはアクターが必要な権限を持っていない場合に発生するエラー｀。

**詳細**
このエラーは、指定された所有者（`_owner`）またはアクター（`_actor`）が指定された権限（`_permission`）を持っていない場合にトリガーされます。
これは、権限管理システムにおいて、アクセス権の不足または不正なアクセス試行を示すために使用されます。

**引数**
- `_owner`
    - 権限の所有者のアドレス。
- `_actor`
    - 行為を試みるアクターのアドレス。
- `_permission`
    - 必要とされる権限を表す`uint256`値。

---

### DuplicatedPermission

```javascript
error DuplicatedPermission(uint256 _permission);
```

**概要**
権限セット間での競合が発生した場合に発生するエラー。

**詳細**
このエラーは、権限が重複している、または相反する場合にトリガーされます。
これは、権限の割り当てや更新プロセスにおいて、一貫性のないまたは不正な権限セットが試みられたことを示します。

**引数**
- `_permission`
    - 競合が発生した権限を表す`uint256`値。

---

### OutOfRange

```javascript
error OutOfRange();
```

**概要**
データが範囲外である場合に発生するエラー。

**詳細**
このエラーは、関数やメソッドの引数が許容範囲外である場合にトリガーされます。
これは、入力データが不正であるか、システムの期待する範囲を超えていることを示します。
範囲外のデータは、プロセスの実行を妨げる可能性があります。

---

# 補足

議論の必要があります。

# 実装

参考実装は以下に格納されています。

https://eips.ethereum.org/assets/eip-6366/contracts/EIP6366Core.sol

# セキュリティ

議論の必要があります。

# 引用

Chiro (@chiro-hiro), Victor Dusart (@vdusart), "ERC-6366: Permission Token [DRAFT]," Ethereum Improvement Proposals, no. 6366, January 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6366.

# 最後に

今回は「256ビットの数値でアクセス権を管理し、効率的かつ柔軟な権限管理の仕組みを提案している規格である**ERC6366**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4