🔑
Python で学ぶビットコインの WIF(Wallet Import Format)アルゴリズム
WIF とは
WIF[1] は、BIP178[2] で定義されているビットコインで使用される ECDSA 秘密鍵をエンコーディングする方法です。WIF は秘密鍵のバックアップやウォレットへ秘密鍵をインポートする際のフォーマットとして利用されています。
秘密鍵 -> WIF
ビットコインの秘密鍵は、乱数から作成された 256bit の整数です。通常は、16 進数で表現するので、64 桁の英数字が並んでいます。
# 秘密鍵の例
0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D
-
mainnet
用秘密鍵には、0x80
をプレフィックスとして付加します。testnet
用秘密鍵には、0xef
をプレフィックスとして付加します。(公開鍵が圧縮形式のときは、さらに0x01
を末尾に付加します。)
# mainnet
800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D
- プレフィックスを付けた秘密鍵にハッシュ関数(
SHA-256
)を2回適用します。
# 1回目
8147786C4D15106333BF278D71DADAF1079EF2D2440A4DDE37D747DED5403592
# 2回目
507A5B8DFED0FC6FE8801743720CEDEC06AA5C6FCA72B07C49964492FB98A714
- ハッシュ値の先頭 4bytes をチェックサムとして「1.」の末尾に付加します。
# 先頭 4bytes
507A5B8D
# チェックサムを付加した値
800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D507A5B8D
- 最後に、Base58Check エンコーディングを行います。
5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ
WIF -> 秘密鍵
WIF から秘密鍵にデコーディングするには、エンコーディングを逆順に実行する必要があります。
- Base58Check デコーディングを行います。
# WIF
5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ
# Decoded WIF
800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D507A5B8D
- チェックサムである末尾の 4bytes を取り除きます。
800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D
- 「2.」で得られた秘密鍵が正しいか検証するために、
SHA-256
を2回適用します。
# 1回目
8147786C4D15106333BF278D71DADAF1079EF2D2440A4DDE37D747DED5403592
# 2回目
507A5B8DFED0FC6FE8801743720CEDEC06AA5C6FCA72B07C49964492FB98A714
- 「3.」で得られたハッシュ値の先頭 4bytes と「2.」で取り除いた値を比較します。同じであれば、正しい秘密鍵であるとわかります。
507A5B8D = 507A5B8D
実験
ビットコインのアドレス作成ができる Python ライブラリ bitcoinaddress
[3] を使用します。
pip3 install bitcoinaddress
>>> from bitcoinaddress import Wallet
>>> wallet = Wallet()
>>> print(wallet)
Private Key HEX: c8e1a0d444b9329bc6d55447ba5c9ca264b8b1cda0b2e55507c7b41b4419316e
Private Key WIF: 5KLkpskNAaxqpFRQzjayZ9hTdQtvdsYvZ2XjthvwK7ic8pCtZr2
Private Key WIF compressed: L3xCSJzQRJuUTYekupxxcuPaXiUBjk7uNMkgKDf4ixee53mmP3Fh
Public Key: 0476131553c4252d5055f5d0185be5cf42597875b7a65139fc73da3d6cb8d0acb5c98aafd9ad7d98d3a0147bdd64276d22f60693feb9e9959c1f372c3e9f6c7926
Public Key compressed: 0276131553c4252d5055f5d0185be5cf42597875b7a65139fc73da3d6cb8d0acb5
Public Address 1: 1JcfvrNbFyGSnHyjCTHRs6sCW7d7Ya5Lhi
Public Address 1 compressed: 12LD7nUPrE2Qa218zQxqvxKnV38ct6Ho7G
Public Address 3: 37mEowyMRmveHvb1ZWLmFGZi36pzbntjM4
Public Address bc1 P2WPKH: bc1qp6dzfnups79z44xfe4ta02r9t6sj4v732rdwct
Public Address bc1 P2WSH: bc1qs8psvr8z8s8hagl95zrlqmtuxqds9hha0ch7h5wgek3efst42wjsf55aav
bitcoinaddress
を使用すると、鍵一式を作ってくれるみたいです。今回は Private Key HEX
を使用して、上記の手順通りに WIF を作ってみて Private Key WIF
と一致することを確認したいと思います。
0x80
を付加した値を SHA-256
に2回適用します。ハッシュ値を求めるには、Python のライブラリを使用してもいいですが、面倒なので Web サイト[4] を使用します。
# 秘密鍵
c8e1a0d444b9329bc6d55447ba5c9ca264b8b1cda0b2e55507c7b41b4419316e
80c8e1a0d444b9329bc6d55447ba5c9ca264b8b1cda0b2e55507c7b41b4419316e
# 1回目
8068F65E851C448438058A40080DC1151F8CB022B1269EB9066963D7675335D9
# 2回目
E497D5E376A78124568E73C28BDA1426D8FFD86CB1D6BDC1EFB4B80B4B49839B
# 先頭 4bytes
E497D5E3
秘密鍵の末尾に 62a04327
を付加した値を Base58Check エンコーディング(Base58 エンコーディングしてくれる Web サイト[5] を使用)します。
80c8e1a0d444b9329bc6d55447ba5c9ca264b8b1cda0b2e55507c7b41b4419316eE497D5E3
# Base58Check エンコーディング結果
5KLkpskNAaxqpFRQzjayZ9hTdQtvdsYvZ2XjthvwK7ic8pCtZr2
最初に導出した WIF 秘密鍵と同じ値になりました。
Discussion