プログラミングを通して知るビットコイン・ブロックチェーン

最近色々な意味でまた騒がれている暗号通貨ですが、技術は非常に面白そうということで、前回は論文を通して P2P ビットコイン取引システムについて学びました。

しかし、あれを読んだだけでは処理部分がよくわからん、と。ブロックチェーンの概念はわかったけれども、詳細な仕組みはどうなってるんだろう、と。

そこで、今回はブロックチェーンを実装・構築することでビットコインの仕組みについてより知識を深めたいと思います。

スポンサーリンク

最初に

使う言語は Python (3.6) です。参考にした記事が Python で書いてたし、一番欠けるのが Python なので都合がいい。

あと、Flask も使います。これも Web アプリ開発で触ってるので都合がいい。素晴らしい。

参考にしたのは、Qiita で今人気のこの記事です。ソースコードはコピペです。

ブロックチェーンを作ることで学ぶ ~ブロックチェーンがどのように動いているのか学ぶ最速の方法は作ってみることだ~ | Qiita

ブロックとは

最初にトランザクションを含んでいるブロックとはどのような構造なのか、例を見てみます。

index, timestamp はわかるとして、そのほかを見てみます。

transaction に含まれるのが、送信者、受信者のアドレスと送信するビットコインの量。previous_hash がハッシュ値。proof は PoW で求められた値 Nonce です。これを見るだけでわくわくしますね。(強制)

previous_hash を含んでいることが重要な点で、この値を含んだ状態でこのブロックでハッシュ値を求め、その値を次のブロックに送信します。これを繰り返すことで、どこかのブロックでハッシュ値が書き換えられたらその後のブロックもハッシュ値を書き換えないと不整合が起きるという構造になるわけです。

ブロックチェーン構築

基本構造

説明はコメントの通り。

トランザクションの追加

ブロックの説明で見た、トランザクションの部分です。論文を見てわかる通り、一つのブロックには複数のトランザクションが含まれているので、current_transaction はリスト型になっています。

ブロックの作成

ジェネシスブロックとは、ブロックチェーンを構成する最初のブロックを指します。

Proof-of-Work (PoW)

ハッシュ値が一定数の 0 で終わる Nonce を求める処理が PoW で、この 0 の数をいじることで演算の難易度を調整することができます。Nonce 自体をプルーフといったり PoW といったりするときもあります。

論文には PoW の仕組みは書いてあるものの、これ自体が値なのかアルゴリズムなのか明確に書かれていなかったのですが、たぶんそんなに重要ではない。

ここで重要なのは、演算は難しく、求めた値が正解か確認するのは簡単であるということ。例えば、RSA 暗号においても平文を暗号化するのは簡単ですが、公開されている情報を基に素数を求めるのは不可能です。この非対称性がセキュリティを高めている、と。

で、この PoW を実装すると次のようになります。

蛇足ですが、valid_proof() 内の f'{last_proof}{proof}' のコーディングについて見かけたことがなく、調べたところ f 文字列というものが Python 3.6 から追加されたようです。これによって文字列内に式を埋め込むことなどが可能になりました。

処理内容から、前の Nonce と次の Nonce を繋げたものをハッシュ化し、0 の個数が条件に合えばそのハッシュ値が Nonce となり、その Nonce が次の Nonce とハッシュ化されて…という流れになっていることがわかります。

サーバサイド

ブロックチェーンの部分は大体完成したので、サーバ側を用意します。

基本構造

トランザクションの追加

Blockchain クラスを利用して書いていきます。

マイニング部分の実装

PoW を実行し、Nonce を求めた人に通貨を発行するトランザクションが発生します。

論文では、ブロック内の最初のチェーンが当該トランザクションになると説明がありましたが、これだと当該トランザクションがチェーンの一番後ろに来ます。PoW のときにハッシュ値の最初の数 bit が 0 という説明においても、ここでは最後の数 bit が 0 になるとしているので、最初と最後の概念が自分の思っているのと逆なのか…?

それはともかく、通貨発行のトランザクションをチェーンに追加し、ブロックを作成します。17 行目について、これは最初のブロックなので blockchain.new_block(proof) となっていますが、この後のブロック作成では blockchain.new_block(proof, previous_hash='xxxx') となるのでしょうか。

コンセンサス

あるノードが求めた Nonce が正しいのかを他のノードが確かめる仕組みと、チェーンの長さから正当なチェーンを探す仕組みを実装します。

後者に関して、ネットワーク上ではブロックが全ノードに到達することを保証しないので、各ノードが持つチェーンの長さにばらつきが出ることがありますが、ここで修正されていきます。

話逸れますが、コンストラクタで定義されている変数に使ってる set() 関数が初見なんですけど、すごく便利そうですね。重複した要素は勝手に取り除くし、集合演算も使えますし。

閑話休題。

エンドポイントも追加します。

テスト

Anaconda で curl しました。ブロックチェーン全体を読み込むならこんな感じです。

Qiita にすべて載ってるので、何も苦労することはないです。逆にコピペだけで面白さはないかもしれませんが。

最後に

年が明けてからの大暴落、つい先日の Coincheck からの XEM 流出事件など、最近また悪い印象が広まっている暗号通貨界隈。成長途中の技術に対するリスクも考えながら進めていかなければならないですね。

スポンサーリンク