paiza開発日誌

IT/Webエンジニア向け総合求人・学習サービス「paiza」(https://paiza.jp ギノ株式会社)の開発者が開発の事、プログラミングネタ、ITエンジニアの転職などについて書いています。

Pythonを使ってビットコインのアドレスを生成したので解説する

f:id:paiza:20180110163713j:plain
Photo by BTC Keychain
秋山です。

最近ビットコインなどの仮想通貨が話題になってますね。エンジニアであれば、ブロックチェーンなどの使われている技術に興味がある…という人も多いのではないでしょうか。

というわけで今回は、ビットコインとそのアドレス生成について書いてみたいと思います。仮想通貨の原型となっているビットコインブロックチェーンやProof of workに少し触れつつ、実際にPythonでコードを書いてビットコインのアドレスをジェネレートしてみます。

ちなみに…Pythonでアドレスをジェネレートするコードには、下記のようなものがあります。ビットコインアスキーアートになってておもしろいですね。(※こちらの記事から引用させていただいております)

_                   =r"""A(W/2,*M(3*G
               *G*V(2*J%P),G,J,G)+((M((J-T
            )*V((G-S)%P),S,T,G)if(S@(G,J))if(
         W%2@(S,T)))if(W@(S,T);H=2**256;import&h
       ashlib&as&h,os,re,bi    nascii&as&k;J$:int(
     k.b2a_hex(W),16);C$:C    (W/    58)+[W%58]if(W@
    [];X=h.new("rip           em    d160");Y$:h.sha25
   6(W).digest();I$                 d=32:I(W/256,d-1)+
  chr(W%256)if(d>0@"";                  U$:J(k.a2b_base
 64(W));f=J(os.urando       m(64))        %(H-U("AUVRIxl
Qt1/EQC2hcy/JvsA="))+      1;M$Q,R,G       :((W*W-Q-G)%P,
(W*(G+2*Q-W*W)-R)%P)       ;P=H-2**       32-977;V$Q=P,L=
1,O=0:V(Q%W,W,O-Q/W*                      L,L)if(W@O%P;S,
T=A(f,U("eb5mfvncu6                    xVoGKVzocLBwKb/Nst
zijZWfKBWxb4F5g="),      U("SDra         dyajxGVdpPv8DhEI
qP0XtEimhVQZnEfQj/       sQ1Lg="),        0,0);F$:"1"+F(W
 [1:])if(W[:1           ]=="\0"@""        .join(map(B,C(
  J(W))));K$:               F(W          +Y(Y(W))[:4]);
   X.update(Y("\4"+                     I(S)+I(T)));B$
    :re.sub("[0OIl    _]|            [^\\w]","","".jo
     in(map(chr,ra    nge    (123))))[W];print"Addre
       ss:",K("\0"+X.dig    est())+"\nPrivkey:",K(
         "\x80"+I(f))""";exec(reduce(lambda W,X:
            W.replace(*X),zip(" \n&$@",["","",
               " ","=lambda W,",")else "])
                    ,"A$G,J,S,T:"+_))

preshing.com

上記のコードを実行すると、ビットコインのアドレスと、それに対応する秘密鍵が生成されます。現段階でビットコインのアドレスとは何ぞ?という人には何を言っているのか分からないかと思いますが、詳しくはこのあと解説します。(ちなみに、このコードで作ったビットコインのアドレスはあんまり使わないほうがよいかもしれないです。記事を書いた方には申し訳ないですが、どこかに秘密鍵を送信するコードが紛れているかも…)

ブロックチェーンとは

ブロックチェーンについては詳しい資料がたくさん出ているので、詳細まで知りたい方はそちらを見たほうがいいかと思いますが…(ブロックチェーン - Wikipedia

ざっくり超簡単に言うと、取引データがブロックというデータ単位で管理されています。各ブロックは時系列順に並び、必ず一つ前のデータのハッシュ値を含んだハッシュ値を持っています。

ブロックチェーンに興味があってちゃんと勉強したい人は、後でリンク貼りますが『Mastering Bitcoin』を読むといいでしょう。

■Proof of workとは

Proof of workは、直訳すると「労力の証明」という意味になりますが、ざっくり超簡単に言うと、データのハッシュ値をある一定のルールに従った値になるように、 nonce と呼ばれるランダムな変数を探す作業…という感じです。(プルーフ・オブ・ワークシステム - Wikipedia

nonceは、あるルールに従ったハッシュ値を生成するためだけに存在します。

ビットコインでは、これまでの Proof of work の完了時間などから、 平均10分程度で Proof of work (nonceを探す作業)が完了するように、 difficulty(難度)が調整されています。(※各ブロックのハッシュ値の頭から何番目までが 0 で埋まっているかで、difficultyが決まります)

例えば、先週は「10桁目までは0で埋める」というルールでnonceを探したけど平均10分を割っていて簡単すぎだったから、今週のブロックは「11桁まで0で埋める」というルールでいく…みたいな感じで調整されています。


ブロックチェーンとProof of workを組み合わせた技術が、ビットコインのコアな部分になるかと思います。

もちろん細かく見ていくとほかにもいろいろな要素はありますが、とりあえず、今までの取引データをもとに調整されたルールに従ってハッシュ値の取得を目指し、みんながいろいろな nonce を使ったハッシュ計算をして、正解を探している…ということですね。

これを改ざんしようと思ったら、該当の取引を改ざんするだけでなく、以降現在のブロックへとつながり得るハッシュ値をすべて計算しなければならず、ものすっごい計算力の高いスーパーコンピューターでもない限り不可能…という感じなので、今のところは改ざんに耐性があると言われています。


ビットコインについてもっと詳しく知りたい人は、少し前のものですが、個人的にはこちらのスライドがわかりやすくて全体が掴めるかと思います。

www.slideshare.net

あとはさっきも言いましたが、Mastering Bitcoinという有名な書籍がおすすめです。和訳版のPDFも無料で読めます。
Translations – Mastering Bitcoin

Mastering Bitcoin: Programming the Open Blockchain

Mastering Bitcoin: Programming the Open Blockchain

※和訳版

ビットコインとブロックチェーン:暗号通貨を支える技術

ビットコインとブロックチェーン:暗号通貨を支える技術

■アドレスについて

では、実際にブロックに入ってる送受金データで使われるアドレスって何なんだろう…という話です。

アドレスは、必ず1か3で始まります。1で始まるアドレスは標準のアドレス、3で始まるアドレスはマルチシグアドレスといいます。

標準のアドレスでは、アドレスと秘密鍵が一対となっており、秘密鍵を所有する人が、そのアドレスにあるビットコインを移動させることができます。

対してマルチシグアドレスでは、アドレスに対して秘密鍵が複数あり、そのうち何個以上を持っている人だけが、そのアドレスにあるビットコインを移動させられるようになっています。秘密鍵が複数必要なので、標準のアドレスに比べるとセキュリティ的に堅牢であるとされています。

マルチシグアドレスの話は結構複雑なため、まずは標準的な1で始まるアドレスを実際に作ってみましょう。

Pythonを使って標準のアドレスを作ってみる

基本的には、秘密鍵をランダム生成し、そこからビットコインアドレスを作る…というやり方になります。

ビットコインのアドレスで用いられる秘密鍵は、楕円曲線DSAのsecp256k1を使います。(Secp256k1 - Bitcoin Wiki

openssl ecparam -genkey -name secp256k1 -out privatekey.pem

opensslが入っている環境であれば、上記のコマンドを叩けば鍵ができます。

これだけで終わりではなく、ビットコインのアドレス形式を作るには、まだいくつか加工手順が必要です。

hash160 と呼ばれる手順を使い、 SHA-256 でダイジェストを得て、そこからさらに RIPEMD160 でダイジェストを得ましょう。

アドレスと秘密鍵の両方におこなう処理としては

  • 先頭に 0 をつける(ビットコインであるというヘッダ的な意味があります、アドレスが必ず1で始まる理由でもあります)
  • チェックサムとして、sha256を2回行ったダイジェストの先頭4バイトを後ろに付与する
  • base58 (i、l、1や0、Oのようなわかりにくい文字種を除外した変換)を行う

上記を行うと、アドレスが生成されます。

以下、ctypesでopensslを使ってアドレスを生成するコードをPythonで書いてみました。(Python3を使うとbytes周りの処理が面倒になったので、Python2で書いています)

#encoding:utf-8
import hashlib
import ctypes
import ctypes.util
import sys
ssl = ctypes.cdll.LoadLibrary (ctypes.util.find_library('ssl'))
 
#sslでsecp256k1の新しい鍵を作る
ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
secp256k1 = ssl.EC_KEY_new_by_curve_name(714) #secp256k1 = 714 で新しい鍵作る
ssl.EC_KEY_generate_key(secp256k1)
 
#アドレス用のpubkeyを得る
size = ssl.i2o_ECPublicKey(secp256k1, 0)
mb = ctypes.create_string_buffer(size)
ssl.i2o_ECPublicKey(secp256k1, ctypes.byref(ctypes.pointer(mb)))
pubkey = mb.raw.rjust(32, chr(0))
 
#秘密鍵を得る
bn = ssl.EC_KEY_get0_private_key(secp256k1);
bytes = (ssl.BN_num_bits(bn) + 7) / 8
mb = ctypes.create_string_buffer(bytes)
n = ssl.BN_bn2bin(bn, mb)
secret = mb.raw
 
#アドレスをsha256でダイジェスト取得してさらにRIPEMD-160でダイジェスト取得
h1 = hashlib.new('ripemd160')
h1.update(hashlib.sha256(pubkey).digest())
hash160 = h1.digest()
 
#base58にエンコードする関数
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def base58_encode(s, version=0): #versionはbitcoinのアドレスですよという感じのヘッダ。アドレスでは先頭が必ず1
    vs = chr(version) + str(s)
    #checksum バージョン+アドレスのsha256を2回かけたやつの先頭バイト
    check = hashlib.sha256(hashlib.sha256(vs).digest()).digest()[:4]
    s = vs + check
    n = int('0x' + s.encode('hex'), 16)
    l = []
    while n > 0:
        n, r = divmod(n, 58)
        l.insert(0,(b58_digits[r]))
    res = ''.join(l)
 
    pad = 0
    for c in s:
        if c == chr(0):
            pad += 1
        else:
            break
    return b58_digits[0] * pad + res
 
#base58にエンコードして完成!(base58_encodeではエンコードとさらにチェックサム付与などしてます)
version = 0
address = base58_encode(hash160, version)
secretkey = base58_encode(secret, 128+version)
print(address,secretkey)
 
ssl.EC_KEY_free(secp256k1)

これを実行すると、アドレスと秘密鍵の一対が作成されます。

paiza.IOなどのオープンな環境でも、もちろん実行自体はできます…が、基本的にそういった環境で作ったアドレスは他の人にも公開されているわけですから、使わないほうがいいでしょう。(実行&使用する際はくれぐれもご注意ください…)基本的にアドレス生成は、盗まれる可能性の低い手元の環境でやってください。

実際に実行してみた結果が以下になります(何かの間違いで送金とかされないようアドレスは黒塗りにしてあります)。
f:id:paiza:20180110131037p:plain


これ以降、会社のマシンを使ってビットコインウォレットを動かすのも何なので、検証にはこちらのWebウォレットを使っていきます。
Bitcoin Block Explorer - Blockchain

登録してログインすると…
f:id:paiza:20180110160530p:plain

設定のアドレスのところに、アドレスのインポートという項目があります。クリックすると以下のようなダイアログが出るので、さきほど生成した秘密鍵を貼り付けてみます。
f:id:paiza:20180110160643p:plain

あとはインポートボタンを押せば、以下のようにアドレスが認識されて表示されるかと思います。(アドレスは入力せず、秘密鍵のみでビットコインアドレスが表示されます)
f:id:paiza:20180110160704p:plain

こんな感じで、ビットコインのアドレスとその秘密鍵が作られて使えるようになります。

当たり前ですが、アドレスは各種ハッシュ関数を通っており、任意に決めるのは不可能です。

しかし、数撃ちゃいつかは当たるかもしれない…というわけで、アドレスを生成しまくって好きなワードを含んだアドレスを探せる vanitygen というツールがあります。

こんな感じでソースのリポジトリが公開されています。
github.com
当然ですが、もしこういったコードに鍵を盗むコードがあった場合は、ビットコインが盗まれてしまう可能性もあります。よく用心したい方は、ソースコードをしっかり読んでから実行するようにしましょう。

使い方は、

git clone https://github.com/exploitagency/vanitygen-plus

cd vanitygen-plus

make

./vanitygen -i 1Paiza

とコマンドを入れて少し待つと

Difficulty: 31912593

Pattern: 1Paiza                                                                

Address: 1PAizA54L~~中略~~Bh

Privkey: 5JDL31~~中略~~Hg

といった感じで、結果を得ることができます。( -iオプションをつけると、大文字小文字問わずに探します)

MacBookPro2016のi7で実行すると、例えば5文字のアルファベット(大文字小文字問わず)の組み合わせなら、2分ほどで見つけられるかと思います。6文字を超えると1時間以上かかったり、10文字の組み合わせで探そうとすると40年ぐらいかかってしまうようです。(人間が1年の間に落ちてきた隕石にぶつかって死ぬ確率は25万分の1らしいですが、アドレスが全く同じになってしまう確率はそれよりも低いので安心してください(?))

あとはマイニングと似たような話になりますが、GPUを何台も使ったりすることもできるため、「どうしてもお気に入りのアドレスがほしい!」という人は、グラフィックボードをたくさん買えば希望がかなうかもしれませんね。

■まとめ

今回は、私がPythonが好きなのでアドレス生成もPythonでやってみました。

繰り返しになりますが、くれぐれも秘密鍵は誰にも見られないようにしてください。クレジットカードなどであれば保険がきくケースもありますが、ビットコインは不可逆なので…。


paizaラーニング」では、今まで有料だった「Python入門編」・「ITエンジニアの就活準備編」が、今月から【全編無料】となりましたのでぜひごらんください。

詳しくはこちら
paizaラーニング





paizaラーニング」では、未経験者でもブラウザさえあれば、今すぐプログラミングの基礎が動画で学べるレッスンを多数公開しております。

そして、paizaでは、Webサービス開発企業などで求められるコーディング力や、テストケースを想定する力などが問われるプログラミングスキルチェック問題も提供しています。

スキルチェックに挑戦した人は、その結果によってS・A・B・C・D・Eの6段階のランクを取得できます。必要なスキルランクを取得すれば、書類選考なしで企業の求人に応募することも可能です。「自分のプログラミングスキルを客観的に知りたい」「スキルを使って転職したい」という方は、ぜひチャレンジしてみてください。

paizaのスキルチェック





※このブログで紹介しているキャンペーンやイベント、およびサイト内の情報については、すべて記事公開時の情報となります。閲覧されたタイミングによっては状況が変わっている場合もございますのでご了承ください。

ITプログラマー・エンジニア転職・就活・学習のpaiza

プログラミング入門講座|paizaラーニング

PHP入門編Ruby入門編Python入門編Java入門編JavaScript入門編C言語入門編C#入門編アルゴリズム入門編

エンジニアのためのプログラミング転職サイト|paiza転職

プログラミング スキルチェックエンジニア求人一覧

未経験からエンジニアを目指す人の転職サイト|EN:TRY

プログラミング スキルチェックエンジニア未経験可求人一覧

エンジニアを目指す学生の就活サイト|paiza新卒

プログラミング スキルチェックエンジニア求人一覧

ブラウザを開くだけで エディタ、Webサーバ、DB等の開発環境が整う|PaizaCloud