ブロックチェーンにおいて新しいブロックを作成するマイニングをPythonを使って体験しましょう。

マイニングには適切に条件を満たすナンスを求める必要があります。下の関数を使ってナンスを求めてみましょう。
import hashlib
previous_block = {
'transactions': 'AtoB=>200yen',
'previous_hash': 'previous_hash',
'nonce': 4
}
def finding_nonce(transactions, difficulty):
previous_hash = hashlib.sha256(str(previous_block).encode()).hexdigest()
nonce = 0
test_block = {
'transactions': transactions,
'previous_hash': previous_hash,
'nonce': nonce
}
test_hash = hashlib.sha256(str(test_block).encode()).hexdigest()
while test_hash[:difficulty] != '0'*difficulty:
test_block = {
'transactions': transactions,
'previous_hash': previous_hash,
'nonce': nonce
}
test_hash = hashlib.sha256(str(test_block).encode()).hexdigest()
nonce += 1
return nonce
前提条件

previous_block = {
'transactions': 'AtoB=>200yen',
'previous_hash': 'previous_hash',
'nonce': 4
}
前提条件としてブロックチェーンの直前のブロック(previous_block)を上のようにおきます。ブロック内のに記述されている情報は主に下の3つでした。
- 前のブロックから得られるハッシュ値(’previous_hash’)
- ナンス(’nonce’)
- 取引内容(’transactions’)
簡単のため’AtoB=>200yen’という取引内容でブロックを作成し、ハッシュ値は’previous_hash’という文字列で、ナンスは4とします。
ナンスを見つける
適切なナンスとは生成したブロックのハッシュ値が’000…’と0が続くような値です。そのようなナンスを下のfinding_nonce関数を使って探します。
finding_nonce関数はブロックに記述する取引内容とマイニングの難易度を引数にとって、適切なナンスを返す関数となっています。
def finding_nonce(transactions, difficulty):
previous_hash = hashlib.sha256(str(previous_block).encode()).hexdigest()
nonce = 0
test_block = {
'transactions': transactions,
'previous_hash': previous_hash,
'nonce': nonce
}
#(略)
まずは新たに生成するブロックを記述します。ナンスはひとまず0とおいてナンスの値を変えていき適切な値を探し出すことができたらブロックチェーン上に載せます。そのためtest_blockという名前にしています。

test_block内のハッシュ値は前のブロックをハッシュ化することで計算することができます。
ハッシュ化にはPythonの標準ライブラリのhashlibを用います。例えば「hello_world」のハッシュ値を計算すると下のようになります。
import hashlib
#hexdigest()は16進数形式の文字列にする
hash_hello_world = hashlib.sha256('hello world'.encode()).hexdigest()
print(hash_hello_world)
#=> b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
ハッシュ関数にはSHA-256というブロックチェーンでよく使われるハッシュ関数を用います。
続いてtest_blockのナンスが適切かを判定します。正しいナンスであればtest_blockをハッシュ化したハッシュ値(test_hash)が’000…’のように0が続くはずです。
test_hash = hashlib.sha256(str(test_block).encode()).hexdigest()
while test_hash[:difficulty] != '0'*difficulty:
test_block = {
'transactions': transactions,
'previous_hash': previous_hash,
'nonce': nonce
}
test_hash = hashlib.sha256(str(test_block).encode()).hexdigest()
nonce += 1
return nonce
そのためwhileループを使ってナンスの探索を行います。
while test_hash[:difficulty] != '0'*difficulty:
はtest_blockのハッシュ値の前から何個かが全て0であるかをループの条件としています。何個0が続くかはマイニングの難易度(difficulty)で指定します。
ハッシュ値が0が続かない場合はナンスを1足して再びハッシュ値を計算して正しいナンスかを判定します。そして正しいナンスが得られた場合はその値を出力します。
実際に動かす
それでは関数を使ってみましょう。ここでは’AtoC=>100yen’という取引内容をブロックに記述します。マイニングの難易度を調整した時の関数の実行時間変化もみてみましょう。
%time finding_nonce('AtoC=>100yen', 3)
#CPU times: user 1.67 ms, sys: 0 ns, total: 1.67 ms
#Wall time: 1.66 ms
#515
%time finding_nonce('AtoC=>100yen', 6)
#CPU times: user 47.4 s, sys: 9.99 ms, total: 47.4 s
#Wall time: 47.5 s
#17980278
難易度が3、つまりブロックをハッシュ化したときに最初の3文字が0であるナンスを探索する場合ナンスは515で、探索時間は1.66msでした。
難易度を6に変えるとナンスは17980278というかなり大きな数となり、探索時間も47.5sと先ほどの1万倍の時間を要する結果となりました。
実際には難易度を調整してナンスの探索時間が10分程度になるように設計されています。このように正しいナンス値が得られるとまた新たなブロックがブロックチェーン上に作成されていきます。
コメント