Python で Bitcoin(仮想通貨)アドレスを作る【Python3】

まずは、ベースとなる形だけ記述しておきます。
(今後使用するモジュールは最初に全部書いておきました)
イメージとしては、このコードが実行された時に秘密鍵、公開鍵、アドレスを作り出します。

#!/usr/bin/env python3
# coding: utf-8
import secrets
import ecdsa
import hashlib
import base58

class Generater():
    def __init__(self):
        p = 2**256-2**32-2**9-2**8-2**7-2**6-2**4-1
        privkey = self.new_privkey()
        pubkey = self.new_pubkey()
        address = self.new_address()

    def new_privkey(self, p):
        pass

    def new_pubkey(self, privkey):
        pass

    def new_address(self, version, pubkey):
        pass

address = Generater()

  

秘密鍵

では順番に見ていきます。まずは、秘密鍵です。
秘密鍵は

\begin{align} p = 2^{256} - 2^{32} - 2^9 - 2^8 - 2^7 - 2^6 - 2^4-1 \end{align}

p より小さい数字(ランダムに選び出した)になります。

    def new_privkey(self, p):
            privkey = secrets.randbelow(p)
            privkey = format(privkey, 'x')
            print("PrivateKey = " + privkey)
            return privkey
名称 内容
randbelow 引数より小さい数字をランダムに選んでくれます。乱数の選び方は、OSによって依存しているので、安全に乱数を出力できるようにしましょう
format 16進数表示に変換します
bytes.fromhex 16進数からバイト列に変換します

  

公開鍵

続いて、公開鍵の説明になります。公開鍵は先ほどの秘密鍵とECDSAを用いて生成します。関数 new_pubkey に引数として先ほどの秘密鍵を指定します。

    def new_pubkey(self, privkey):
            bin_privkey = bytes.fromhex(privkey)
            signing_key = ecdsa.SigningKey.from_string(bin_privkey, curve = ecdsa.SECP256k1)
            verifying_key = signing_key.get_verifying_key()
            pubkey = bytes.fromhex("04") + verifying_key.to_string()
            pubkey = pubkey.hex()
            print("PublicKey = " + pubkey)
            return pubkey
名称 内容
bytes.fromhex 16進数からバイト列に変換します
ecdsa.SigningKey ECDSAを用いて楕円曲線から公開鍵となる値を計算する
pubkey プレフィックスとして16進数で"04"を先頭に、ECDSAで計算結果列を後ろに付けます。最後に全体を16進数表記にすれば完成

  

アドレス

アドレスは、今回はビットコインアドレスの作り方を真似ました。引数として、先ほどの公開鍵とバージョンを指定するようにしています。ビットコインアドレスの場合16進数で "00" になります。

    def new_address(self, version, pubkey):
            ba = bytes.fromhex(pubkey)
            digest = hashlib.sha256(ba).digest()
            new_digest = hashlib.new('ripemd160')
            new_digest.update(digest)
            pubkey_hash = new_digest.digest()

            pre_address = version + pubkey_hash
            address = hashlib.sha256(pre_address).digest()
            address = hashlib.sha256(address).digest()
            checksum = address[:4]
            address = pre_address + checksum
            address = base58.b58encode(address)
            address = address.decode()
            print("Address = " + address + "\n")
            return address

  
ここから注意点がたくさん出てきます。まず、アドレスを作る際にチェックサムが必要になります。チェックサムを生成するために、公開鍵のハッシュ値が必要です。

\begin{align} pubkeyhash = ripemd160(sha256((pubkey))) \end{align}

  
また、チェックサムは  

\begin{align} address = sha256(sha256(payload)) \end{align}

  
この address の前から 4バイトになります。

\begin{align} checksum = address[:4] \end{align}

  

名称 内容
ba 16進数公開鍵をバイト列に変換
digest baをsha256でハッシュ化してできたものを文字列にして代入
pubkey_hash digestをripemd160でハッシュ値を取り、文字列として代入
pre_address version(引数) + pubkey_hash
b58encode b58encodeする
decode デコードする

これで以上になります。全体像を最後に貼り付けておきます。

     

#!/usr/bin/env python3
# coding: utf-8
import secrets
import ecdsa
import hashlib
import base58

class Generater():
    def __init__(self):
        p = 2**256-2**32-2**9-2**8-2**7-2**6-2**4-1
        privkey = self.new_privkey(p)
        pubkey = self.new_pubkey(privkey)
        address = self.new_address(bytes.fromhex("00"), pubkey)

    def new_privkey(self, p):
        privkey = secrets.randbelow(p)
        privkey = format(privkey, 'x')
        print("PrivateKey = " + privkey)
        return privkey

    def new_pubkey(self, privkey):
        bin_privkey = bytes.fromhex(privkey)
        signing_key = ecdsa.SigningKey.from_string(bin_privkey, curve = ecdsa.SECP256k1)
        verifying_key = signing_key.get_verifying_key()
        pubkey = bytes.fromhex("04") + verifying_key.to_string()
        pubkey = pubkey.hex()
        print("PublicKey = " + pubkey)
        return pubkey

    def new_address(self, version, pubkey):
        ba = bytes.fromhex(pubkey)
        digest = hashlib.sha256(ba).digest()
        new_digest = hashlib.new('ripemd160')
        new_digest.update(digest)
        pubkey_hash = new_digest.digest()

        pre_address = version + pubkey_hash
        address = hashlib.sha256(pre_address).digest()
        address = hashlib.sha256(address).digest()
        checksum = address[:4]
        address = pre_address + checksum
        address = base58.b58encode(address)
        address = address.decode()
        print("Address = " + address + "\n")
        return address

address = Generater()


では、実行して見ましょう。

PrivateKey = c31ad13427f28c429c443d6058129051614875ab5f576364e0e2866e3ab87aae
PublicKey = 048813b6143a1316041ff1fb4f3af6061a05f7803ef01e32cf3ff1a71422f95a6d600638c460a49f5c6125b11cbc5bdfff30c0545c6f208d9a1fa6ed504b2d2c29
Address = 176wVvyL4NzecB9nm7DmuQ3x5rfqXCo1n5

  
Address が「1」から始まっていれば成功です。