どうも、月見(@Suzuka14144156)です。
私は、ブロックチェーンに関する研究をしており、本ブログでは仮想通貨/暗号資産の情報を整理して発信しています。
今回は、Pythonでブロックチェーンを実際に実装していきたいと思います。
参考にした講座は以下のUdemyの講座です。
こちらの講座は、Pythonでスクラッチでブロックチェーンを実装するというものです。
Pythonの中級者以上の向けの講座となっています。
ブロックチェーンだけでなく、クラスを使ったコーディングを覚えたい方にもオススメの講座だと思います。
前回までの私の記事は、こちらです。
今回は、前回からの続きですので、お読みでない方は、ぜひ参考にしてみてください。
【Python】ブロックチェーンの実装_1 クラスの定義
【Python】ブロックチェーンの実装方法_2 hashの定義
【Python】ブロックチェーンの実装方法_3 トランザクションの定義
今回は、Proof of Workの実装です。
Proof of Workとは?
- Proof of Workとはコンセンサスアルゴリズムの一つ
- コンセンサスアルゴリズムとは、暗号資産(仮想通貨)の基盤技術となるブロックチェーンがブロックを追加する際のルール
PoWは以下3つの要素を加算し、そのハッシュ値の最初の3桁が000(ケタ数は任意)になったときのchallengeの値をnouneに記述します。
このハッシュ値がどのようになったときを正しいとするかは、アルゴリズムによります。
今は、仮で000としています。
当然、ケタ数が多ければ多いほど、計算時間はかかります。
- challenge
- prev hash
- transaction
challengeは正の整数です。
これを1、2…と代入していくことでnonceの値を探索するというアルゴリズムです。
Proof of Workの実装
それでは、早速実装します。
def valid_proof(self, transactions, previous_hash, nonce,
difficulty=MINING_DIFFICULTY):
guess_block = utils.sorted_dict_by_key({
'transactions': transactions,
'nonce': nonce,
'previous_hash': previous_hash
})
guess_hash = self.hash(guess_block)
return guess_hash[:difficulty] == '0'*difficulty
valid_proofのメソッドにより、ハッシュ値が000の時は、Trueをリターンします。
def proof_of_work(self):
transactions = self.transaction_pool.copy()
previous_hash = self.hash(self.chain[-1])
nonce = 0
while self.valid_proof(transactions, previous_hash, nonce) is False:
nonce += 1
return nonce
proof_of_workのメソッドにより、valid_proofからTrueが帰ってくるまでnonceに+1を加算し続けます。
これによって、000になるときのnonceを探索するアルゴリズムとなります。
utils.py
import collections
def sorted_dict_by_key(unsorted_dict):
return collections.OrderedDict(
sorted(unsorted_dict.items(), key=lambda d: d[0]))
ディクショナリーのitemの順序を整列かさせるための関数です。
詳細は、以下の記事を参考にしてみてください。
【Python】ブロックチェーンの実装方法_2 hashの定義
blockchains.py
import hashlib
import json
import logging
import sys
import time
import utils
MINING_DIFFICULTY = 3
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
class BlockChain(object):
def __init__(self):
self.transaction_pool = []
self.chain = []
self.create_block(0, self.hash({}))
def create_block(self, nonce, previous_hash):
block = utils.sorted_dict_by_key({
'timestamp': time.time(),
'transactions': self.transaction_pool,
'nonce': nonce,
'previous_hash': previous_hash
})
self.chain.append(block)
self.transaction_pool = []
return block
def hash(self, block):
sorted_block = json.dumps(block, sort_keys=True)
return hashlib.sha256(sorted_block.encode()).hexdigest()
def add_transaction(self, sender_blockchain_address,
recipient_blockchain_address, value):
transaction = utils.sorted_dict_by_key({
'sender_blockchain_address': sender_blockchain_address,
'recipient_blockchain_address': recipient_blockchain_address,
'value': float(value)
})
self.transaction_pool.append(transaction)
return True
def valid_proof(self, transactions, previous_hash, nonce,
difficulty=MINING_DIFFICULTY):
guess_block = utils.sorted_dict_by_key({
'transactions': transactions,
'nonce': nonce,
'previous_hash': previous_hash
})
guess_hash = self.hash(guess_block)
return guess_hash[:difficulty] == '0'*difficulty
def proof_of_work(self):
transactions = self.transaction_pool.copy()
previous_hash = self.hash(self.chain[-1])
nonce = 0
while self.valid_proof(transactions, previous_hash, nonce) is False:
nonce += 1
return nonce
def pprint(chains):
for i, chain in enumerate(chains):
print(f'{"="*25} Chain {i} {"="*25}')
for k, v in chain.items():
if k == 'transactions':
print(k)
for d in v:
print(f'{"-"*40}')
for kk, vv in d.items():
print(f' {kk:30}{vv}')
else:
print(f'{k:15}{v}')
print(f'{"*"*25}')
if __name__ == '__main__':
block_chain = BlockChain()
pprint(block_chain.chain)
block_chain.add_transaction('A','B', 1.0)
previous_hash = block_chain.hash(block_chain.chain[-1])
nonce = block_chain.proof_of_work()
block_chain.create_block(nonce, previous_hash)
pprint(block_chain.chain)
block_chain.add_transaction('C','D', 2.0)
block_chain.add_transaction('X','Y', 3.0)
previous_hash = block_chain.hash(block_chain.chain[-1])
nonce = block_chain.proof_of_work()
block_chain.create_block(nonce, previous_hash)
pprint(block_chain.chain)
実行結果は以下です。
========================= Chain 0 =========================
nonce 0
previous_hash 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
timestamp 1630818254.176903
transactions
*************************
========================= Chain 0 =========================
nonce 0
previous_hash 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
timestamp 1630818254.176903
transactions
========================= Chain 1 =========================
nonce 5699
previous_hash 4be633c7836d1242f81142e28bb26b1f720e76c8f7e52e366d272d85cb6f7e86
timestamp 1630818254.7045
transactions
----------------------------------------
recipient_blockchain_address B
sender_blockchain_address A
value 1.0
*************************
========================= Chain 0 =========================
nonce 0
previous_hash 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
timestamp 1630818254.176903
transactions
========================= Chain 1 =========================
nonce 5699
previous_hash 4be633c7836d1242f81142e28bb26b1f720e76c8f7e52e366d272d85cb6f7e86
timestamp 1630818254.7045
transactions
----------------------------------------
recipient_blockchain_address B
sender_blockchain_address A
value 1.0
========================= Chain 2 =========================
nonce 4905
previous_hash 123d9ce45d48712365d1732f0c0eae276dfe75e615f031df5956053be56205c3
timestamp 1630818255.035936
transactions
----------------------------------------
recipient_blockchain_address D
sender_blockchain_address C
value 2.0
----------------------------------------
recipient_blockchain_address Y
sender_blockchain_address X
value 3.0
*************************
コメント