Base58Check のエンコードとデコードの方法
エンコードするには:
- 入力 · Hex(16進数)に 16進数を貼り付けます。先頭がバージョンバイトで、その後にペイロードが続きます。空白、コロン、
0x接頭辞も受け付けます。 - 結果を出力 · Base58Check で確認します。ツールがチェックサムを計算し、エンコードの前に付け足します。
- Bitcoin とほとんどのフォークでは、アルファベットを Bitcoin のままにしておきます。
デコードするには、方向の切り替えで Base58Check → Hex(16進数)をクリックし、アドレスや鍵を貼り付けます。デコーダーは変換してからチェックサムを計算し直し、4 バイトのチェックサムがすべて一致した場合にだけペイロード(バージョンバイトから始まるまま)を返します。1 文字でも入力ミスがあると、「チェックサムが一致しません。文字列の入力ミスか破損です。」と表示されます。
Base58Check が Base58 に加えるもの
素の Base58 には誤り検出がありません。入力ミスがあっても、別の、しかし同じく有効な文字列になるだけです。Base58Check は 2 つの追加でこれを解決します。
version (1 byte) | payload | first 4 bytes of SHA-256(SHA-256(version | payload))
全体をその後 Base58 でエンコードするので、先頭のゼロバイトは 1 になります。バージョンバイトはネットワークとデータの種類を決め、数の先頭に置かれるため、結果の最初の文字も決まります。
バージョンバイトと接頭辞
同じ 20 バイトのハッシュ 62e907b15cbf27d5425399ebf6f0fb50ebb88f18 に、異なるバージョンバイトを付けた場合です。
| バージョン | 意味 | 最初の文字 | 結果 |
|---|---|---|---|
00 | Bitcoin の P2PKH アドレス | 1 | 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa |
05 | Bitcoin の P2SH アドレス | 3 | 3Ai1JZ8pdJb2ksieUV8FsxSNVJCpoPi8W6 |
6f | テストネットの P2PKH アドレス | m または n | mpXwg4jMtRhuSpVq4xS3HFHmCmWp9NyGKt |
c4 | テストネットの P2SH アドレス | 2 | 2N2GDNJ4rEm6NxfMC9ck8VuRdheQzXWaNZv |
1 行目は、Bitcoin のジェネシスブロックの報酬を受け取ったアドレスです。他の行はバージョンバイトの説明のためだけのものです。P2SH はスクリプトをハッシュするので、公開鍵ハッシュを P2SH のバージョンバイトで包んでも、使える P2SH アドレスにはなりません。
WIF 秘密鍵
Wallet Import Format は、バージョンバイト 80 と 32 バイトの秘密鍵による Base58Check です。末尾の 01 バイトは、公開鍵を圧縮形式で使う鍵であることを示します。Bitcoin wiki のよく知られた例の鍵では次のようになります。
| 16進数の入力 | Base58Check |
|---|---|
80 + 0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D | 5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ |
同じもの + 01 | KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617 |
非圧縮の WIF 鍵は 5 で始まり、圧縮形式のものは K か L で始まります。この例の鍵は公開されているので、実際の資金には決して使わないでください。
Python での Base58Check
チェックサムは標準ライブラリの SHA-256 を 2 回呼ぶだけです。小さな Base58 エンコーダーと組み合わせると次のようになります。
import hashlib
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def b58encode(data: bytes) -> str:
n = int.from_bytes(data, "big")
out = ""
while n:
n, r = divmod(n, 58)
out = ALPHABET[r] + out
zeros = len(data) - len(data.lstrip(b"\0"))
return "1" * zeros + out
def b58check(payload: bytes) -> str:
checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
return b58encode(payload + checksum)
b58check(bytes.fromhex("0062e907b15cbf27d5425399ebf6f0fb50ebb88f18"))
# '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
コードで文字列を検証するには、Base58 でデコードし、最後の 4 バイトを切り離して、残りのダブルハッシュと比較します。Python でのバイト列の扱いについては Python のページをご覧ください。
よくある問題
- チェックサムが一致しない。 文字列に入力ミスがあるか、途中で切れているか、チェックサムのない素の Base58 です(Solana のアドレスや IPFS のハッシュは素の Base58 なので、Base58 デコーダーを使ってください)。
- 「4 バイトのチェックサムを含むには短すぎます」。 デコードした値が 4 バイト未満なので、Base58Check ではありえません。
- 予期しない文字
"0"、"O"、"I"、"l"。 Base58 のアルファベットにない文字です。書き写しを確認してください。 - エンコード後の最初の文字が違う。 バージョンバイトを確認してください。バージョンバイトがないと、20 バイトのハッシュはたいてい
1で始まらない文字列になります。
よくある質問
Base58Check のチェックサムはどう計算されますか?
ペイロード(バージョンバイトを含む)を SHA-256 でハッシュし、その結果をもう一度 SHA-256 でハッシュして、最初の 4 バイトを取ります。この 4 バイトを付け足してから Base58 でエンコードします。
チェックサムが有効なら、そのアドレスに送金しても安全ですか?
わかるのは、文字列に入力ミスがないということだけです。ランダムな入力ミスが通ってしまう確率は約 43 億分の 1 です。誰がそのアドレスを管理しているか、思っているネットワークのものかどうかについては何もわかりません。
Base58Check で bc1 のアドレスをデコードできますか?
できません。bc1 で始まるアドレスは Bech32 または Bech32m(SegWit と Taproot)で、別のアルファベットと別のチェックサムを使います。
本物の秘密鍵をここに貼り付けてもいいですか?
変換はブラウザ内で行われ、ページは Cloudflare のページビュー計測を除くすべてのネットワーク通信をブロックしています。それでも WIF 鍵は資金を動かせる鍵です。価値のあるものを扱う場合は、テスト用の鍵かオフラインのマシンを使ってください。