いまや知らない人はいない仮想通貨、別名暗号通貨(cryptocurrency)。
ドル円とは比べ物にならないほどの殺人的なボラティリティを見せているけれど、しばらくすれば落ち着いてくるんだろうか。
はっきりいって今の相場と税率でレバレッジ15倍とか正気の沙汰とは思えない。樹海の中に宝探しに行くようなものだ。
今回の記事の本題はその仮想通貨の仕組みについて。
投資にも興味はあるが、仮想通貨の仕組み自体にも興味がある。
仮想通貨の仕組みを知れば投資先の選択にも参考になるんじゃないかなと思うし、なにより以前から技術的に面白そうだと思っていた。
その仮想通貨の基盤となる技術がブロックチェーンというもので、これを理解しないことには始まらない。ということでファーストステップとしてブロックチェーンを理解するためにコードを写経してみた。
写経するのはid:mizchi氏の記事
コード自体はjavascriptで書かれているものを、アレンジを加えつつpython(python3.5)に書き直してみる。
(そっくりなタイトルを見てわかる通りまあただの写経。)
大元のソースはこれnaivechain/main.js at master · lhartikk/naivechain · GitHub
ザックリとした説明
ブロックチェーンとは
ブロックチェーンは書き換えができない
ID番号は計算によって出てくるものなんだけど、計算するにあたって前のブロックのID番号や自身のブロックの情報を使うことになる。
なので途中のブロックの情報を書き換えるとそれ以降に続いているブロックのID番号が計算と合わなくなってしまう。これがブロックの書き換えができないと言われている大きな理由。
コード
blockchain.pyというファイル名で保存
import hashlib import time from datetime import datetime class Block: def __init__(self, index, previous_hash, data, timestamp, this_hash): self.index = index self.previous_hash = previous_hash self.data = data self.timestamp = timestamp self.this_hash = this_hash def equal(self, block): if not self.index == block.index: return False elif not self.previous_hash == block.previous_hash: return False elif not self.data == block.data: return False elif not self.timestamp == block.timestamp: return False elif not self.this_hash == block.this_hash: return False return True class Blockchain: def __init__(self): self.blockchain = [self.get_initial_block()] def get_initial_block(self): return Block(0, "0", "my genesis block!!", 1465154705, "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7") def get_length(self): return len(self.blockchain) def get_latest_block(self): return self.blockchain[-1] def generate_next_block(self, block_data): previous_block = self.get_latest_block() next_index = previous_block.index + 1 next_timestamp = int(time.mktime(datetime.now().timetuple())) next_hash = calculate_hash(next_index, previous_block.this_hash, block_data, next_timestamp) return Block(next_index, previous_block.this_hash, block_data, next_timestamp, next_hash) def is_valid_new_block(self, new_block, previous_block): if previous_block.index + 1 != new_block.index: return False elif previous_block.this_hash != new_block.previous_hash: return False elif calculate_hash_for_block(new_block) != new_block.this_hash: return False return True def is_valid_chain(self): if not self.blockchain[0].equal(self.get_initial_block()): return False else: tmp_block = self.blockchain[0] for i in range(1,self.get_length()): if not self.is_valid_new_block(self.blockchain[i], tmp_block): return False else: tmp_block = self.blockchain[i] return True def add_block(self, new_block): if self.is_valid_new_block(new_block, self.get_latest_block()): self.blockchain.append(new_block) else: raise ValueError("add block error!") def calculate_hash(index, previous_hash, data, timestamp): string = str(index) + previous_hash + data + str(timestamp) return hashlib.sha256(string.encode('utf-8')).hexdigest() def calculate_hash_for_block(block): return calculate_hash(block.index, block.previous_hash, block.data, block.timestamp)
簡単に動かしてみる
同じフォルダ内で呼び出して使う
from blockchain import * # ブロックチェーンのインスタンス blockchain = Blockchain() # 新しいブロック new_block = blockchain.generate_next_block("Hello World!") # ブロックを加える try: blockchain.add_block(new_block) except ValueError as e: print(e) # もう一つ加える new_block = blockchain.generate_next_block("hoge") try: blockchain.add_block(new_block) except ValueError as e: print(e)
競合ブロックを作って追加するテスト
先ほどの続きに書く
もし何かの手違いで新しいブロックを生成するタイミングが被ってしまうとどうなるか、その時は同じ「前のブロックチェーンのID番号」を持ったブロックができてしまう。そのようなブロックを追加する場合はエラーが出て弾かれるようにしたい。*2
競合ブロックを作ってみて、2つ目のブロックが追加できないことを確認する
block1 = blockchain.generate_next_block("original block") block2 = blockchain.generate_next_block("second block") try: blockchain.add_block(block1) except ValueError as e: print(e) try: blockchain.add_block(block2) print("競合テストNG") except ValueError as e: print("競合テストOK!") # ここでエラーになれば成功
- 結果
競合テストOK!
途中のブロックを書き換えるテスト
途中のブロックのデータを書き換えて、ブロックチェーンが不適切だと判定されることを確認する
# 各々のブロックのデータを見てみる print("-----Block data------") for i in range(blockchain.get_length()): print(blockchain.blockchain[i].data) # 途中のブロックのデータを書き換える blockchain.blockchain[2].data = "invalid message!!!!" print("-----Block data (changed)------") for i in range(blockchain.get_length()): print(blockchain.blockchain[i].data) # 今のブロックチェーンが妥当かどうか検証 if not blockchain.is_valid_chain(): print("書き換えテストOK!") else: print("書き換えテストNG")
- 実行結果
-----Block data------ my genesis block!! Hello World! hoge original block -----Block data (changed)------ my genesis block!! Hello World! invalid message!!!! original block 書き換えテストOK!