[ERC1191] チェーンIDをEthereumのチェックサムに活用する仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、チェーンIDを活用したEthereumアドレスのチェックサムの仕組みを提案している規格である**ERC1191**についてまとめていきます！

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

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

:::note warn
**1191**は現在（2023年12月30日）では「**Last Call**」段階です。
:::

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

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

# 概要

この提案（EIP）は、イーサリアムで資金を安全に送るためのルールを改善するためのものです。
最初に作られたルール（**EIP55**）は、アドレスに大文字と小文字が混在しているかどうかをチェックして、ユーザーが間違ったアドレスにお金を送らないようにするものでした。

しかし、この新しい提案では、さらに安全性を高めるために、**EIP155**で定められた「**chain id**」というものを使って、チェックサムの計算に加えることを提案しています。
チェーンIDは、イーサリアムの異なるネットワーク（例えば、メインネットやテストネットなど）を識別するためのIDです。
これを使うことで、ユーザーが間違えて別のネットワークのアドレスにお金を送ってしまうリスクを減らせます。
例えば、実際のお金が動くメインネットから、実験用のテストネットのアドレスに誤って送金してしまうような間違いを防げるようになります。

このように、チェーンIDを使ったチェックサム計算を取り入れることで、ユーザーがより安心してイーサリアムを使えるようになると期待されています。

:::note info
### チェックサム
イーサリアムのアドレスは基本的に、`0`から`9`の数字と`a`から`f`の文字を組み合わせた16進数の`40`文字からなります。
例えば、「`0x123abc...`」のような形です。
しかし、これらのアドレスは非常に長く、似たような文字が多いため、誤って間違ったアドレスに送金してしまうリスクがあります。
そこで、チェックサムという仕組みが導入されました。

#### チェックサムとは？

チェックサムは、アドレスの正確性を確認するための追加的な情報です。
イーサリアムでは、**EIP55**という提案により、アドレスの一部の文字を大文字と小文字を使い分けることでチェックサムを表現しています。
この小文字と大文字の使い分けによって、誤ったアドレスに送金するリスクを大幅に減らすことができます。

#### 具体例

例えば、あるイーサリアムアドレスが「`0xabcdef123456...`」という形をしているとします。
チェックサムを適用すると、このアドレスは例えば「`0xAbCdEf123456...`」のように変換されるかもしれません。
ここで、`a`が`A`に、`b`が小文字のまま、`c`が`C`に、`d`が小文字のまま、`e`が`E`に、`f`が小文字のまま変換されています。

この変換はどのように決まるのかというと、アドレス自体から計算されるハッシュ値を基にしています。
アドレスを特定の方法でハッシュ化し（具体的には**keccak-256**アルゴリズムを使用）、そのハッシュ値の各ビットを見て、アドレスの各文字を大文字にするか小文字にするかを決めます。

#### なぜ重要なのか？

このチェックサムを使うことで、ユーザーがアドレスを手入力する際に起こりがちな小さな間違い（例えば、`5`を`S`と間違えるなど）があった場合、そのアドレスはチェックサムに違反するため、ウォレットやアプリケーションは警告を出して、ユーザーが間違った送金をしてしまうのを防ぐことができます。

イーサリアムのチェックサムは、ユーザーが安全にアドレスを扱うための非常に重要な機能であり、誤送金のリスクを大幅に減らすことができるため、広く採用されています。
:::

:::note info
**EIP55**については以下の記事を参考にしてください。
:::

https://qiita.com/cardene/items/985f03760df12929b5f9

:::note info
**EIP155**については以下の記事を参考にしてください。
:::

https://qiita.com/cardene/items/2526432899353e4a0225

# 動機

この提案は、イーサリアムのさまざまなネットワーク（例えば、実際に取引が行われるメインネットや、テスト用のテストネットなど）に属するアドレスを、ソフトウェアが簡単に見分けられるようにするためのものです。
イーサリアムのアドレスは、単に公開キーから生成されたハッシュなので、どのネットワークに属するアドレスなのかを外から見ただけでは分かりません。
このため、人々は間違ったネットワークにお金を送ってしまうリスクがあります。

この提案では、**EIP55**（アドレスのチェックサムを行う既存の方法）を改良して、アドレスに「**どのネットワークに属しているか**」の情報を加えることができるようにしようとしています。
これにより、ソフトウェアはアドレスを見ただけで、それがどのネットワークのものなのかを識別できるようになります。
この機能があれば、ユーザーが誤って異なるネットワークに資金を送るリスクを大幅に減らすことができ、より安心してイーサリアムを利用できるようになります。

# 仕様

この手順は、イーサリアムのアドレスをより特定の方法で識別しやすくするための新しい方法です。
基本的には、**EIP55**という既存のルールを使ってアドレスの正確さをチェックしますが、さらに新しいステップを加えています。
もし特定のイーサリアムネットワークを示すチェーンIDがあれば、そのIDをデータ処理の一部として加えます。
これは、異なるイーサリアムのネットワークを区別するのに役立ちます。

具体的には、もしチェーンIDがこの新しい方式を使うネットワークのものであれば、アドレスの前にそのチェーンIDと「`0x`」という文字を足してからデータを処理します。
次に、アドレスを16進数形式に変換しますが、16進数の各文字（`abcdef`のいずれか）について、特定のルールに基づき大文字か小文字かを決めます。
そのルールとは、特定の計算結果のビットが1かどうかを見て、1なら大文字、そうでなければ小文字にするというものです。

:::note info
このルールは、イーサリアムのアドレスをチェックサムする時に使われるものです。
アドレスを16進数形式で表した時、各文字（数字か`abcdef`のいずれか）に対して大文字か小文字かを決めるために使用します。
ここでいう「**特定の計算結果のビット**」とは、アドレスから計算されたハッシュ値のことです。

具体例を見てみましょう。
仮にあるアドレスの16進数表記が「`a1b2c3`」という形だったとします。
このアドレスに対してチェックサムを行うためには、まずハッシュ関数を使ってこのアドレスからハッシュ値を計算します。
このハッシュ値もまた16進数で表され、たとえば「`3f2a1b...`」のような形になります。

次に、元のアドレスの各文字に対応するハッシュ値のビットを見ていきます。最初の文字は「`a`」で、これは16進数の`10`を意味します。
ハッシュ値の対応する位置（この場合は最初の文字）をビットで見た時、`4`倍の位置（この場合は`40`番目のビット）が`1`かどうかを確認します。
もし`1`ならば、「`a`」は大文字の「`A`」に、`0`ならば小文字の「`a`」のままにします。

このプロセスをアドレスの各文字に対して行い、最終的には例えば「`A1B2c3`」という形のチェックサム済みアドレスが得られます。
この方法により、アドレスが正確に入力され、かつ正しいネットワークに属していることを確認するのに役立ちます。
:::

この新しい方法により、ソフトウェアはイーサリアムのアドレスがどのネットワークに属しているかをより正確に判断できるようになります。
これは、ユーザーが間違ったネットワークにお金を送ってしまうリスクを減らすことに役立ち、全体的に安全で確実な取引を促進します。

# 補足

この提案の大きなメリットは、ごくわずかなプログラムの更新だけで、イーサリアムの異なるネットワーク（例えば、メインネットとテストネットなど）のアドレスを間違えて混同し、結果としてお金を失ってしまうというユーザーのリスクを減らすことができる点です。
実際には、既に使われているプログラムに少し手を加えるだけで、はるかに安全にイーサリアムを使えるようになります。
これにより、人々が誤って別のネットワークにお金を送るという間違いを効率的に防げるようになり、安心してイーサリアムを利用できるようになります。

# 実装

```python
#!/usr/bin/python3
from sha3 import keccak_256
import random
"""
   addr (str): Hexadecimal address, 40 characters long with 2 characters prefix
   chainid (int): chain id from EIP-155 """
def eth_checksum_encode(addr, chainid=1):
    adopted_eip1191 = [30, 31]
    hash_input = str(chainid) + addr.lower() if chainid in adopted_eip1191 else addr[2:].lower()
    hash_output = keccak_256(hash_input.encode('utf8')).hexdigest()
    aggregate = zip(addr[2:].lower(),hash_output)
    out = addr[:2] + ''.join([c.upper() if int(a,16) >= 8 else c for c,a in aggregate])
    return out
```

このPythonスクリプトは、イーサリアムのアドレスに対してチェックサムを適用するための関数 `eth_checksum_encode` を提供します。
このチェックサムは、特定のネットワーク（この場合は**RSK Mainnet**と**RSK Testnet**、それぞれチェーンIDが`30`と`31`）に対して特別な処理を行います。

### 関数
- **関数名**
    - `eth_checksum_encode`
- **引数**
  - `addr`
      - チェックサムを適用するイーサリアムのアドレス（16進数形式、'0x'プレフィックス付き、40文字）。
  - `chainid`
      - **EIP155**に基づくチェーンID。
      - デフォルトは`1`（イーサリアムメインネット）。

### 処理
1. **EIP-1191の適用チェック**:
   - チェーンIDが**EIP1191**を採用しているかどうか（この場合は`30`または`31`）を確認します。
   - 採用している場合は、ハッシュ計算の入力としてチェーンIDとアドレスを結合します。
   - それ以外の場合は、アドレスのプレフィックス（'0x'）を除いたものを使います。

2. **ハッシュ計算**
   - `keccak_256`ハッシュ関数を使用して、上記で得られた文字列のハッシュ値を計算します。
   - このハッシュ値は、アドレスの各文字が大文字か小文字かを決定するために使用されます。

3. **チェックサムの適用**
   - アドレスとハッシュ値を組み合わせて、各文字が大文字か小文字かを決定します。
   - 具体的には、アドレスの各16進数文字に対して、対応するハッシュ値のビットが8以上（16進数で8）であれば大文字、それ以外は小文字にします。

4. **出力の生成**
   - 変換された文字を結合して、チェックサムが適用されたアドレスを生成します。
   - このアドレスは、元の'0x'プレフィックスに続けて出力されます。

### まとめ
このスクリプトは、イーサリアムのアドレスにチェックサムを適用し、特定のネットワークに対して特別な処理を行うことで、誤ったネットワークへの送金を防ぐのに役立ちます。
**EIP1191**を採用しているネットワークでは、より具体的なチェックサムが適用され、誤送金のリスクをさらに低減します。

# テスト

```python
eth_mainnet = [
"0x27b1fdb04752bbc536007a920d24acb045561c26",
"0x3599689E6292b81B2d85451025146515070129Bb",
"0x42712D45473476b98452f434e72461577D686318",
"0x52908400098527886E0F7030069857D2E4169EE7",
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
"0x6549f4939460DE12611948b3f82b88C3C8975323",
"0x66f9664f97F2b50F62D13eA064982f936dE76657",
"0x8617E340B3D01FA5F11F306F4090FD50E238070D",
"0x88021160C5C792225E4E5452585947470010289D",
"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
"0xde709f2102306220921060314715629080e2fb77",
"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
]
rsk_mainnet = [
"0x27b1FdB04752BBc536007A920D24ACB045561c26",
"0x3599689E6292B81B2D85451025146515070129Bb",
"0x42712D45473476B98452f434E72461577d686318",
"0x52908400098527886E0F7030069857D2E4169ee7",
"0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD",
"0x6549F4939460DE12611948B3F82B88C3C8975323",
"0x66F9664f97f2B50F62d13EA064982F936de76657",
"0x8617E340b3D01Fa5f11f306f4090fd50E238070D",
"0x88021160c5C792225E4E5452585947470010289d",
"0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB",
"0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB",
"0xDe709F2102306220921060314715629080e2FB77",
"0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359",
]
rsk_testnet = [
"0x27B1FdB04752BbC536007a920D24acB045561C26",
"0x3599689e6292b81b2D85451025146515070129Bb",
"0x42712D45473476B98452F434E72461577D686318",
"0x52908400098527886E0F7030069857D2e4169EE7",
"0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd",
"0x6549f4939460dE12611948b3f82b88C3c8975323",
"0x66f9664F97F2b50f62d13eA064982F936DE76657",
"0x8617e340b3D01fa5F11f306F4090Fd50e238070d",
"0x88021160c5C792225E4E5452585947470010289d",
"0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB",
"0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB",
"0xDE709F2102306220921060314715629080e2Fb77",
"0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359",
]
test_cases = {30 : rsk_mainnet, 31 : rsk_testnet, 1 : eth_mainnet}

for chainid, cases in test_cases.items():
    for addr in cases:
        assert ( addr == eth_checksum_encode(addr,chainid) )
```

このテストコードは、前述の `eth_checksum_encode` 関数が正しくイーサリアムのアドレスにチェックサムを適用しているかどうかを検証するためのものです。
具体的には、異なるイーサリアムベースのネットワーク（イーサリアムメインネット、RSKメインネット、RSKテストネット）に対してチェックサムを行い、期待される結果が得られるかを確認しています。

### スクリプト

- **アドレスリスト**
  - `eth_mainnet`
      - イーサリアムメインネットのアドレスのリスト。
  - `rsk_mainnet`
      - RSKメインネットのアドレスのリスト。
  - `rsk_testnet`
      - RSKテストネットのアドレスのリスト。

- **テストケースの設定**
  - `test_cases`
      - 各ネットワークのチェーンIDと、そのネットワークのアドレスリストをマッピングしています。
      - ここでは、イーサリアムメインネット（チェーンID: `1`）、RSKメインネット（チェーンID: `30`）、RSKテストネット（チェーンID: `31`）が設定されています。

- **テストの実行**
  - `for` ループを使用して、`test_cases` に設定された各ネットワークのチェーンIDとアドレスリストに対してテストを行います。
  - 各アドレスに対して `eth_checksum_encode` 関数を呼び出し、入力したアドレスと関数から返されたアドレスが同じかどうかを `assert` 文で確認します。
  - もし一致しない場合は、エラーが発生してテストが失敗します。

### まとめ
このテストコードは、異なるネットワークに属するイーサリアムアドレスに対して、`eth_checksum_encode` 関数が正しくチェックサムを適用できているかを検証します。
すべてのアドレスで `assert` 条件が真となれば、関数は正常に動作していると考えられます。
これにより、関数が想定通りにアドレスのチェックサムを計算できることを確かめることができます。

# 使用方法

## Usage Table

| Network     | Chain id | Supports this EIP |
|-------------|----------|-------------------|
| RSK Mainnet | 30       | Yes               |
| RSK Testnet | 31       | Yes               |

## Implementation Table

| Project             | EIP Usage | Implementation   |
|---------------------|-----------|------------------|
| MyCrypto            | Yes       | JavaScript       |
| MyEtherWallet       | Yes       | JavaScript       |
| Ledger              | Yes       | C                |
| Trezor              | Yes       | Python and C     |
| Web3.js             | Yes       | JavaScript       |
| EthereumJS-util     | Yes       | JavaScript       |
| ENS address-encoder | Yes       | TypeScript       |

# 引用

Juliano Rizzo (@juli), "ERC-1191: Add chain id to mixed-case checksum address encoding [DRAFT]," Ethereum Improvement Proposals, no. 1191, March 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1191.

# 最後に

今回は「チェーンIDを活用したEthereumアドレスのチェックサムの仕組みを提案している規格である**ERC1191**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下の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