まずは、ベースとなる形だけ記述しておきます。
(今後使用するモジュールは最初に全部書いておきました)
イメージとしては、このコードが実行された時に秘密鍵、公開鍵、アドレスを作り出します。
address.py
#!/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()
秘密鍵
では順番に見ていきます。まずは、秘密鍵です。
秘密鍵は
p = 2^{256} - 2^{32} - 2^9 - 2^8 - 2^7 - 2^6 - 2^4-1
p より小さい数字(ランダムに選び出した)になります。
address.py
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 に引数として先ほどの秘密鍵を指定します。
address.py
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" になります。
address.py
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
ここから注意点がたくさん出てきます。まず、アドレスを作る際にチェックサムが必要になります。チェックサムを生成するために、公開鍵のハッシュ値が必要です。
pubkeyhash = ripemd160(sha256((pubkey)))
また、チェックサムは
address = sha256(sha256(payload))
この address の前から 4バイトになります。
checksum = address[:4]
名称 | 内容 |
---|---|
ba | 16進数公開鍵をバイト列に変換 |
digest | baをsha256でハッシュ化してできたものを文字列にして代入 |
pubkey_hash | digestをripemd160でハッシュ値を取り、文字列として代入 |
pre_address | version(引数) + pubkey_hash |
b58encode | b58encodeする |
decode | デコードする |
これで以上になります。全体像を最後に貼り付けておきます。
address.py
#!/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').zfill(64)
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」から始まっていれば成功です。