Pythonでブロックチェーンを学ぶ -2-
以下、前回の第1回目で書いた Proof of Work の実装を用いて、実際にTransaction処理を書いてみる。
ブロックに追加するデータは、仮想通貨であれば、送金元アドレス、振込先アドレス、コインの数などになるが、ここでは、実験として Virtual Machine3台に定期的にポーリングして CPU値、Memory値、時刻を取得して、ブロックチェーンに追加していく処理にしてみた。(これらを対象としたのに特に大きな意味はないですが。。)
※上記 Raspberry Pi上で グラフ(SenseHat) のコードはここでは割愛。
ブロックチェーンの処理
関数は以下の構成。
==========================================
- Class 定義
- コンストラクタ
- 新しいブロックチェーン作成
- 新しいトランザクションをリストに加える
- プルーフ・オブ・ワークのTrue/False 判断
- ノードリストに新ノードを追加
- ブロックチェーンが正しいかを確認
- ノード間でのチェーンの整合性確認
- ブロックの SHA-256 ハッシュを作る
- 最初の4つが0となるような ノンス値を探す
- チェーンの最後のブロックをリターンする
==========================================
# coding: UTF-8
import hashlib
import json
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request
from urllib.parse import urlparse
import requests
class Blockchain(object):
def __init__(self):
"""
コンストラクトの設定。ブロックを作る初期設定
"""
self.chain = []
self.current_transactions = []
self.nodes = set()
self.new_block(previous_hash=1, proof=100)
def new_block(self, proof, previous_hash=None):
"""
ブロックチェーンに新しいブロックを作る
:param proof: <int> プルーフ・オブ・ワークアルゴリズムから得られるプルーフ
:param previous_hash: (オプション) <str> 前のブロックのハッシュ
"""
block = {
'index': len(self.chain) + 1,
'timestamp': time(),
'transactions': self.current_transactions,
'proof': proof,
'previous_hash': previous_hash or self.hash(self.chain[-1]),
}
# 現在のトランザクションをリセット
self.current_transactions = []
# 新しいブロックを作り、チェーンに加える
self.chain.append(block)
return block
def new_transaction(self, cpuPct, maxMemAlloc, memFree, modTs):
"""
新しいトランザクションをリストに加える
次に採掘されるブロックに加える新しいトランザクションを作る
:param cpuPct: <int> CPU使用率
:param maxMemAlloc: <int> メモリ割当量
:param memFree: <int> Freeメモリ
:param modT: <int> 上記パラメーターの最終更新時間
"""
self.current_transactions.append({
'cpuPct': cpuPct,
'maxMemAlloc': maxMemAlloc,
'memFree': memFree,
'modTs': modTs
})
return self.last_block['index'] + 1
def proof_of_work(self, last_proof):
"""
プルーフ・オブ・ワークのアルゴリズム:
- hash(xx') の最初の4つが0となるような x' を探す
- x は1つ前のブロックのプルーフ、 x' は新しいブロックのプルーフ
:param last_proof: <int>
:return: <int>
"""
proof = 0
while self.valid_proof(last_proof, proof) is False:
proof += 1
return proof
def register_node(self, address):
"""
ノードリストに新しいノードを加える
:param address: <str> ノードのアドレス 例: 'http://192.168.0.5:5000'
:return: None
"""
parsed_url = urlparse(address)
self.nodes.add(parsed_url.netloc)
def valid_chain(self, chain):
"""
ブロックチェーンが正しいかを確認する
:param chain: <list> ブロックチェーン
:return: <bool> True であれば正しく、 False であればそうではない
"""
last_block = chain[0]
current_index = 1
while current_index < len(chain):
block = chain[current_index]
print(f'{last_block}')
print(f'{block}')
print("\n-------------------\n")
#ブロックのハッシュが正しいかを確認
if block['previous_hash'] != self.hash(last_block):
return False
# PoWが正しいかどうかを確認
if not self.valid_proof(last_block['proof'], block['proof']):
return False
last_block = block
current_index += 1
return True
def resolve_conflicts(self):
"""
コンセンサスアルゴリズム
ネットワーク上の最も長いチェーンで置き換える
これによって複数ノードで同期した場合のコンフリクトを解消する
:return: <bool> 自らのチェーンが置き換えられると True 、そうでなれけば False
"""
neighbours = self.nodes
new_chain = None
"""
自らのチェーンより長いチェーンを探す
"""
max_length = len(self.chain)
"""
他のすべてのノードのチェーンを確認
"""
for node in neighbours:
response = requests.get(f'http://{node}/chain')
print(response)
if response.status_code == 200:
length = response.json()['length']
chain = response.json()['chain']
"""
そのチェーンがより長いか、有効かを確認
"""
if length > max_length and self.valid_chain(chain):
max_length = length
new_chain = chain
"""
もし自らのチェーンより長く、かつ有効なチェーンを見つけた場合それで置き換える
"""
if new_chain:
self.chain = new_chain
return True
return False
@staticmethod
def hash(block):
"""
ブロックの SHA-256 ハッシュを作る
:param block: <dict> ブロック
:return: <str>
"""
#必ずdict objectがソートされている必要がある。
block_string = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()
@staticmethod
def valid_proof(last_proof, proof):
"""
hash(xx') の最初の4つが0となるような x' を探す
"""
guess = f'{last_proof}{proof}'.encode()
# f文字列について-> http://atsuoishimoto.hatenablog.com/entry/2016/12/25/122220
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:4] == "0000"
@property
def last_block(self):
# チェーンの最後のブロックをリターンする
return self.chain[-1]
次回は ノード上でリクエスト処理を行うための APIを実装してみます。
今回の"note"を気に入って頂けましたら、是非サポートをお願いいたします!