はじめに

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

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

今回は、Ethereumでの大文字と小文字が混在するアドレスのチェックサムの仕組みを提案している規格であるEIP55についてまとめていきます!

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

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

仕様

python
import eth_utils


def checksum_encode(addr): # Takes a 20-byte binary address as input
    hex_addr = addr.hex()
    checksummed_buffer = ""

    # Treat the hex address as ascii/utf-8 for keccak256 hashing
    hashed_address = eth_utils.keccak(text=hex_addr).hex()

    # Iterate over each character in the hex address
    for nibble_index, character in enumerate(hex_addr):

        if character in "0123456789":
            # We can't upper-case the decimal digits
            checksummed_buffer += character
        elif character in "abcdef":
            # Check if the corresponding hex digit (nibble) in the hash is 8 or higher
            hashed_address_nibble = int(hashed_address[nibble_index], 16)
            if hashed_address_nibble > 7:
                checksummed_buffer += character.upper()
            else:
                checksummed_buffer += character
        else:
            raise eth_utils.ValidationError(
                f"Unrecognized hex character {character!r} at position {nibble_index}"
            )

    return "0x" + checksummed_buffer


def test(addr_str):
    addr_bytes = eth_utils.to_bytes(hexstr=addr_str)
    checksum_encoded = checksum_encode(addr_bytes)
    assert checksum_encoded == addr_str, f"{checksum_encoded} != expected {addr_str}"


test("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")
test("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")
test("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB")
test("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb")

このコードは、イーサリアム(Ethereum)のアドレスをチェックサムエンコードするためのPython関数と、それをテストするための関数を提供しています。 以下の手順に従ってコードの機能と動作を詳細に説明します。

checksum_encode

  • checksum_encode関数は、20バイトのバイナリアドレスを受け取り、チェックサム付きの形式で表現します。
  • まず、入力のバイナリアドレスを16進数文字列に変換します(addr.hex()を使用)。
  • 次に、16進数文字列をUTF-8エンコードしたものとして扱い、それをkeccak256ハッシュ関数を用いてハッシュ化します。
    • このハッシュ化は、アドレスのチェックサム計算に使用されます。
  • その後、16進数文字列を一文字ずつ処理します。
    • 各文字に対して以下の条件分岐を行います。
    • 数字(0から9)の場合
      • 文字をそのまま追加します。
    • 小文字の16進数文字(aからf)の場合
      • 対応するハッシュ値の対応するニブル(4ビット単位)が8以上の場合、大文字に変換して追加し、そうでない場合は小文字のまま追加します。
      • これにより、チェックサムの計算に寄与する文字を大文字で表現します。
    • 上記以外の文字の場合
      • エラーを発生させます。
      • これにより、アドレス文字列が16進数の文字以外を含む場合にエラーが報告されます。
  • 最終的に、すべての文字を処理したら、0xを接頭辞としてチェックサムエンコードされたアドレス文字列を返します。

test

  • test関数は、アドレス文字列を受け取り、それがchecksum_encode関数によって正しくエンコードされるかをテストします。
  • 与えられたアドレス文字列を16進数のバイト列に変換し、checksum_encode関数を呼び出してチェックサムエンコードを行います。
  • チェックサムエンコードされたアドレス文字列が元のアドレス文字列と一致しない場合、アサーションエラーが発生し、エラーメッセージが表示されます。

最後に、提供されたテストケースでtest関数が呼び出されており、各アドレス文字列が正しくチェックサムエンコードされるかどうかを確認しています。

このコードは、Ethereumのアドレス文字列のチェックサムを計算するために使用できるユーティリティ関数を提供し、それが正しく機能することを確認するテストも提供しています。

この要求に基づいて、アドレスを16進数文字列に変換し、特定の条件に従って大文字または小文字で出力するプロセスを説明します。 また、指定された条件に従った例も示します。

  1. アドレスを16進数文字列に変換します。
    • 例
      • アドレス0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAedは5aaeb6053f3e94c9b9a09f33669435e7ef1beaedに変換されます。
  2. 16進数文字列を小文字に変換し、UTF-8エンコードして、keccak256ハッシュ関数を使用してハッシュ値を計算します。
    • 例
      • ハッシュ化により、バイナリ表現0010 1101 1000 0000 0101 1111 0010 0001 ...が得られます。
  3. ハッシュ値の各ビットを調べ、4ビットごとに文字を処理します。
  4. 各文字が数字(0から9)である場合、そのまま出力します。
    • 例
      • 5は数字であるため、そのまま出力されます。
  5. 各文字が小文字の16進数文字(aからf)である場合、対応するハッシュ値の4ビット目(0から3のビット位置)を確認します。
    • 4ビット目が1であれば、その文字を大文字で出力し、0であれば小文字で出力します。
    • 例1
      • aは小文字であり、ハッシュ値の4ビット目が1であるため、大文字で出力されます(A)。
    • 例2
      • fは小文字であり、ハッシュ値の4ビット目が0であるため、小文字で出力されます(f)。
  6. 上記の条件に当てはまらない場合、そのまま出力します。

上記のプロセスにより、指定された条件に基づいて、アドレスの各16進数文字が大文字または小文字で正確に出力されます。 特定の言葉や略語についても、指定された条件に従って変換されます。

補足

利点

  • 既存の多くの16進数パーサーと互換性があり、大文字と小文字を混在して受け入れることができるため、徐々に導入するのが容易です。
    • これは、従来の16進数アドレス表現と新しいチェックサムエンコード表現の両方が受け入れられることを意味します。
  • アドレスの長さを40文字に保ちます。
    • アドレスの長さは変わらず、従来の形式と同じ40文字です。
  • 平均的にアドレスごとに15個のチェックビットがあり、ランダムに生成されたアドレスが誤ってチェックを通過する確率は約0.0247%です。
    • これはICAPに比べて約50倍の改善であり、4バイトのチェックコードに比べると劣ります。
    • チェックサムを使用することで、アドレスの誤りを検出しやすくし、間違って受け入れられる確率を大幅に減少させます。
    • ただし、4バイトのチェックコードに比べると精度は劣ります。

新しいチェックサムエンコード方法には、アドレス表現の改善に関するいくつかの利点があります。 まず、既存の16進数パーサーとの互換性を保持し、新しい方法を段階的に導入することが容易です。 また、アドレスの長さは40文字のままであり、形式の変更が必要ありません。 さらに、アドレスごとに平均的に15個のチェックビットがあり、ランダムに生成されたアドレスが誤ってチェックを通過する確率は非常に低いため、エラーの検出が向上します。 これにより、アドレスの信頼性が高まりますが、4バイトのチェックコードに比べるとわずかに劣る精度を持っています。

実装

javascript
const createKeccakHash = require('keccak')

function toChecksumAddress (address) {
  address = address.toLowerCase().replace('0x', '')
  var hash = createKeccakHash('keccak256').update(address).digest('hex')
  var ret = '0x'

  for (var i = 0; i < address.length; i++) {
    if (parseInt(hash[i], 16) >= 8) {
      ret += address[i].toUpperCase()
    } else {
      ret += address[i]
    }
  }

  return ret
}

イーサリアム(Ethereum)のアドレス文字列をチェックサムエンコードするためのJavaScript関数toChecksumAddressを実装しています。 以下はコードの機能と動作の詳細な説明です。

createKeccakHashライブラリのインポート

最初に、createKeccakHashライブラリをrequireを使ってインポートしています。 このライブラリは、Keccak-256ハッシュ関数を提供します。

toChecksumAddress関数

toChecksumAddress関数は、チェックサムを適用するためのメインの関数です。 引数として、チェックサムを適用したいアドレス文字列を受け取ります。

アドレスの前処理

関数内で、受け取ったアドレス文字列を小文字に変換し、0xの接頭辞を取り除きます。 これにより、アドレス文字列が小文字で始まることと、接頭辞0xが存在しないことが保証されます。

アドレスのハッシュ化

  • createKeccakHash('keccak256')を使用して、Keccak-256ハッシュ関数を初期化します。
  • update(address)メソッドを使用して、前処理されたアドレス文字列をハッシュ関数に渡し、ハッシュ値を計算します。
  • digest('hex')メソッドを使用して、ハッシュ値を16進数文字列として取得します。

チェックサムの計算

  • ハッシュ値と前処理されたアドレス文字列を比較し、チェックサムを計算します。
  • forループを使用して、アドレス文字列の各文字に対して以下の条件分岐を行います。
    • アドレス文字列の各文字を16進数に変換し、10進数として取得します(parseInt(hash[i], 16))。
    • 取得した10進数が8以上の場合、対応するアドレス文字を大文字に変換してret変数に追加します。
      • これにより、チェックサムの計算に寄与する文字が大文字で表現されます。
    • それ以外の場合、アドレス文字をそのままret変数に追加します。

チェックサムエンコードされたアドレスの返却

  • ret変数にはチェックサムエンコードされたアドレス文字列が含まれています。
  • 関数は最終的にこの文字列をreturnして返します。

このコードは、イーサリアムのアドレス文字列にチェックサムを適用し、大文字と小文字を適切に変換して、アドレスの正確性を向上させるためのユーティリティ関数を提供しています。

javascript
> toChecksumAddress('0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359')
'0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359'

Keccak-256ハッシュへの入力は、小文字の16進文字列(つまり、ASCIIとしてエンコードされた16進アドレス)であることに注意する。

javascript
var hash = createKeccakHash('keccak256').update(Buffer.from(address.toLowerCase(), 'ascii')).digest()

テスト

text
# All caps
0x52908400098527886E0F7030069857D2E4169EE7
0x8617E340B3D01FA5F11F306F4090FD50E238070D
# All Lower
0xde709f2102306220921060314715629080e2fb77
0x27b1fdb04752bbc536007a920d24acb045561c26
# Normal
0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed
0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359
0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB
0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb

引用

Vitalik Buterin vitalik.buterin@ethereum.org, Alex Van de Sande avsa@ethereum.org, "ERC-55: Mixed-case checksum address encoding," Ethereum Improvement Proposals, no. 55, January 2016. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-55.

最後に

今回は「Ethereumでの大文字と小文字が混在するアドレスのチェックサムの仕組みを提案している規格であるEIP55」についてまとめてきました! いかがだったでしょうか?

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

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