はじめに
巷でビットコインやブロックチェーンというものが流行っているみたいですね。流行に乗り遅れないように (今さらとか言わないでね )、最近 ビットコインとブロックチェーン という本を読み始めました。
この本の中で WIF 形式 (Wallet Import Format) というフォーマットが登場しました。ビットコインのウォレットでは秘密鍵をこの形式で扱います。今回、復習を兼ねて秘密鍵を WIF 形式に変換する処理を Python で実装してみました。
実装
具体的には Bitcoin Wiki の Wallet import format というページに書かれている内容をコードに起こしました。
from base58 import b58encode, b58decode
from binascii import hexlify, unhexlify
from hashlib import sha256
import unittest
def encode_private_key_as_wif(private_key):
"""
秘密鍵 (16 進数文字列) を WIF 形式に変換する。
"""
# 先頭の 0x80 は秘密鍵をエンコードする際の version byte を表す。
key = '80' + private_key
tmp = key
# SHA-256 関数を 2 回適用する。
tmp = sha256(unhexlify(tmp)).hexdigest()
tmp = sha256(unhexlify(tmp)).hexdigest()
# 末尾の 8 バイトをチェックサムとする。
checksum = tmp[0:8]
wif_private_key = b58encode(unhexlify(key + checksum))
return wif_private_key
def decode_wif_private_key(wif_private_key):
"""
秘密鍵 (WIF 形式) を 16 進数文字列に変換する。
"""
tmp = hexlify(b58decode(wif_private_key)).decode('utf-8')
# version byte とチェックサムを取り除く。
private_key = tmp[2:-8]
# b58decode の代わりに b58decode_check を使う場合
# tmp = hexlify(b58decode_check(wif_private_key)).decode('utf-8')
# private_key = tmp[2:]
return private_key
def verify_checksum(wif_private_key):
"""
秘密鍵のチェックサムを検証する。
"""
b58decoded = hexlify(b58decode(wif_private_key)).decode('utf-8')
tmp = b58decoded[:-8]
tmp = sha256(unhexlify(tmp)).hexdigest()
tmp = sha256(unhexlify(tmp)).hexdigest()
return b58decoded[-8:] == tmp[:8]
class WIFTest(unittest.TestCase):
"""
WIF 形式に関する関数の動作を検証するための単体テスト。
検証のための値は https://en.bitcoin.it/wiki/Wallet_import_format から借用している。
"""
def test_encode_private_key_as_wif(self):
private_key = '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
wif_private_key = encode_private_key_as_wif(private_key)
self.assertEqual('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ', wif_private_key)
def test_decode_wif_private_key(self):
wif_private_key = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
private_key = decode_wif_private_key(wif_private_key)
self.assertEqual('0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d', private_key)
def test_verify_checksum(self):
valid_wif_private_key = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
invalid_wif_private_key = 'SHueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
self.assertTrue(verify_checksum(valid_wif_private_key))
self.assertFalse(verify_checksum(invalid_wif_private_key))
if __name__ == '__main__':
unittest.main()
この Python スクリプトを実行するには base58
というライブラリが必要です。
$ pip install base58
なお、スクリプトを実行すると unittest による単体テストが動きます。
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK