どうも、月見(@Suzuka14144156)です。
私は、ブロックチェーンに関する研究をしており、本ブログでは仮想通貨/暗号資産の情報を整理して発信しています。
今回は、Pythonでブロックチェーンを実際に実装していきたいと思います。
参考にした講座は以下のUdemyの講座です。
こちらの講座は、Pythonでスクラッチでブロックチェーンを実装するというものです。
Pythonの中級者以上の向けの講座となっています。
ブロックチェーンだけでなく、クラスを使ったコーディングを覚えたい方にもオススメの講座だと思います。
前回までの私の記事は、こちらです。
今回は、前回からの続きですので、お読みでない方は、ぜひ参考にしてみてください。
【Python】ブロックチェーンの実装_1 クラスの定義
今回は、ハッシュの実装です。
ハッシュとは?
ハッシュ(またはハッシュ値)とは、あるデータを変換して得られる固定長のデータのことです。ハッシュはあるデータを一方向にしか演算できないのが特徴で、ハッシュ化されたデータを元のデータに戻すことはほぼ不可能です。また元のデータを1文字でも変更するとハッシュ化されたデータは全く違う結果となり、元データを推測することを不可能にしてます。
引用元:https://bitflyer.com/ja-jp/glossary/hash
- つまり、元に戻すことができないデータ
これをブロックチェーンで生成し、連結された次のブロックでそのハッシュを格納します。
こうすることで、ブロックとブロックとがハッシュで繋がれる状態になります。
ビットコインで使用されるハッシュは、以下二つです。
- SHA-256
- RIPEMD-160
今回は、SHA-256の挙動をまずは確認したいと思います。
SHA-256
- SHA-256とは、【Secure Hash Algorithm 256-bit】とは、任意の長さの原文から固定長の特徴的な値を算出するハッシュ関数(要約関数)の一つです。

- GPU で計算するのに向いているのが特徴です。
Pythonで実際にコーディングして挙動を確認します。
コード
import hashlib
print(hashlib.sha256('test'.encode()).hexdigest())
hashlibというライブラリを使用します。
その中なのsha256というクラスを呼び出し、エンコードします。
実行結果は以下です。
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
testとという文字列をエンコードするとこのような文字列となります。
しかし、この文字列からtestとデコードするのが不可能です。
これがハッシュです。
この文字列を生成したブロックの次のブロックに格納することで次々にブロック同士をチェーンのように連結することができます。
それを実装します。
ハッシュの格納
collectionsの機能確認
collectionsで、dict型の中身を整列することができます。
例えば、以下二つのディクショナリー型のブロックのハッシュが異なるのはナンセンスです。
- block = {‘a’ : 1,’b’ : 1}
- block2 = {‘b’ : 1,’a’ : 1}
そこで、collectionsで順序を揃えて、hashlibに渡します。
そうすることで、両者のハッシュ値が異なるのを防ぎます。
import collections
import hashlib
block = {'a' : 1,'b' : 1}
block2 = {'b' : 1,'a' : 1}
print('-'*15,'Before collections', '-'*15)
print(block)
print(block2)
print(hashlib.sha256(str(block).encode()).hexdigest())
print(hashlib.sha256(str(block2).encode()).hexdigest())
print('-'*15,'after collections', '-'*15)
block = collections.OrderedDict(sorted(block.items(), key=lambda d:d[0]))
block2 = collections.OrderedDict(sorted(block2.items(), key=lambda d:d[0]))
print(block)
print(block2)
print(hashlib.sha256(str(block).encode()).hexdigest())
print(hashlib.sha256(str(block2).encode()).hexdigest())
実行結果は、以下です。
--------------- Before collections ---------------
{'a': 1, 'b': 1}
{'b': 1, 'a': 1}
923eee0614a9d145b7192237ad8d6d81ba8246e0efa3254798c69d2db8be258e
ccfabf2f584cd6f4e169bf3e04f14e38c7c955cfb567f9d84e3364b2b74bd554
--------------- after collections ---------------
OrderedDict([('a', 1), ('b', 1)])
OrderedDict([('a', 1), ('b', 1)])
0735015f2e8395cf9e1726201de955f0769f510310556088113363eb0a83c44f
0735015f2e8395cf9e1726201de955f0769f510310556088113363eb0a83c44f
hashを追加したコード
utils.py
dictの中身をソートする機能。
これによって、以下が格納されているdictの記述の順番が異なってもソートして、整合されたhash値がリターンされることになります。
- ‘timestamp’: time.time(),
- ‘transactions’: self.transaction_pool,
- ‘nonce’: nonce,
- ‘previous_hash’: previous_hash
import collections
def sorted_dict_by_key(unsorted_dict):
return collections.OrderedDict(
sorted(unsorted_dict.items(), key=lambda d: d[0]))
blockchains.py
import hashlib
import json
import logging
import sys
import time
import utils
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 pprint(chains):
for i, chain in enumerate(chains):
print(f'{"="*25} Chain {i} {"="*25}')
for k, v in chain.items():
print(f'{k:15}{v}')
print(f'{"*"*25}')
if __name__ == '__main__':
block_chain = BlockChain()
pprint(block_chain.chain)
previous_hash = block_chain.hash(block_chain.chain[-1])
block_chain.create_block(5, previous_hash)
pprint(block_chain.chain)
previous_hash = block_chain.hash(block_chain.chain[-1])
block_chain.create_block(2, previous_hash)
pprint(block_chain.chain)
実行結果は、以下です。
previous_hashに前のブロックが生成したhashが格納されています。
block_chain.chain[-1]で一番最後のblock_chain.chainを指定しています。
これによって、Chain1のprevious_hashにChain0によって生成された、hashが格納さています。
同様に、Chain2には、Chain1のhashが格納されます。
これが数珠つなぎになって、Chain nには、Chain(n-1)のhashが格納されます。
つまり、前のチェーンしか次のチェーンがどれなのかを把握することができないというのがこれによって実現できます。
========================= Chain 0 =========================
nonce 0
previous_hash 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
timestamp 1630761584.6910028
transactions []
*************************
========================= Chain 0 =========================
nonce 0
previous_hash 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
timestamp 1630761584.6910028
transactions []
========================= Chain 1 =========================
nonce 5
previous_hash 92db071c0c4707572351ad7b5a0c30a594649f7e4c870a93f4f787a40b0a3c5d
timestamp 1630761584.691303
transactions []
*************************
========================= Chain 0 =========================
nonce 0
previous_hash 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
timestamp 1630761584.6910028
transactions []
========================= Chain 1 =========================
nonce 5
previous_hash 92db071c0c4707572351ad7b5a0c30a594649f7e4c870a93f4f787a40b0a3c5d
timestamp 1630761584.691303
transactions []
========================= Chain 2 =========================
nonce 2
previous_hash 6150274d3e984f4c7d69085adc5d330dbca6f4586287df908398d02dab22d1e6
timestamp 1630761584.691605
transactions []
*************************
今回の記事は、以上です。
最後までお読みいただきありがとうございました。
コメント