ブロックチェーンはビットコインの構成技術としてご存じの方も多いと思いますが、ビットコイン以外にも様々なことに利用されている技術です。
ブロックチェーン技術とは簡単に言えば
取引記録(データ)を「ブロック」という単位でまとめ、そのブロック同士をハッシュ値で鎖のようにつなぎ、ネットワーク全体で共有する分散台帳技術
です。
複数の取引記録をひとまとめにして1つのブロックとして扱います。
どのくらいの取引を1ブロックにまとめるかはブロックチェーンの種類によって異なります。
以下はブロックチェーンの簡易的な図です。
各ブロックは前のブロックのハッシュ値を持つことで、チェーンのように連なっているわけです。
そして過去から現在までのすべてのブロック(取引履歴が)が 1 本のチェーンのように保存されます。

Proof of Workとナンス(Nonce)
ブロックチェーンにはブロックが次々と連なっていくわけですが、新しいブロックをチェーンにつなぐ際には ナンス(Nonce) と呼ばれる値を探索する必要があります。これは Proof of Work(PoW) と呼ばれる仕組みです。
PoWの概要は以下の通りです。
前のブロックのハッシュ値、追加しようとするブロック、ナンス値からブロックヘッダというデータ構造を作る。
ブロックヘッダからハッシュ値を計算し、そのハッシュ値がある条件を満たしていればOKとする。
ある条件とは例えば以下のようなものです。
ハッシュ値が「0000」から始まる
この条件を満たすハッシュ値を見つけるため、ナンス値を変えていき、ひたすら何度もハッシュ値を計算する必要があります。
この作業は非常に計算量が大きいため、マシンパワー(電力)を大量に必要とします。
そしてこのナンス値を見つける作業をマイニングといい、マイニングを行う人をマイナーと呼びます。
さらにこのPoWの難易度は徐々に上がっていくよう調整されます。
例えば条件を「ハッシュ値が「00000」から始まる」に変更すれば難易度はぐんと上がります。
難易度を上げる理由ですが、ネットワーク全体の計算力が増大しても平均ブロック生成時間を一定に保つためです。
ムーアの法則にある通り、コンピュータの計算能力は年々上がっていくので、難易度を変えないとナンス値探索にかかる時間がどんどん速くなっていってしまうわけです。
最初にナンス値を見つけたマイナーには報酬が与えられるため世界中のマイナーが競ってPoWを行っています。
ハッシュ関数には以下の性質があります。
- 入力からハッシュ値を計算するのは容易
- 同じ入力からは同じハッシュ値
- ハッシュ値から元の入力を見つけるのは現実的に不可能 (一方向性)
ハッシュ値の検証自体は一瞬でできるため、適当なナンス値を提出してもネットワークの他のノードが即座に検証してはじきます。
分散台帳という名の通り、ブロックチェーン自体は1か所で管理されているのではなく、世界中で管理(保有)されているので他の人もハッシュ値の検証はできるわけです。
簡易的な実装例
仕組みの理解のため、Pythonでブロックチェーンを簡易的に実装してみます。
ブロックの作成関数
def createBlock(self, nonce, preHash = None):
block = {
'index' : len(self.chain) + 1,
'timestamp' : time.time(),
'nonce' : nonce, # ナンス値
'preHash' : preHash or self.hash(self.chain[-1]), # 1つ前のブロックハッシュ値
'data': f'Block{len(self.chain) + 1}取引データ' # 取引データ
}
self.chain.append(block)
return
こちらはブロックを作成しチェーンに追加する関数です。
個々のブロックはナンス値と一つ前のブロックのハッシュ値を持つようにしています。
ナンス値の探索
def findNonce(self, lastNonce, conditions = '0000'):
nonce = 0
while self.validNonce(lastNonce, nonce, conditions) is False:
# 条件を満たすナンス値が見つかるまで1ずつインクリメントして試す
nonce += 1
return nonce
こちらはナンス値を探索する関数です。ナンス値を0から順番に試していき、条件を満たすナンス値を見つけます。
引数の「conditions」に条件を設定します。「0000」を指定した場合はハッシュ値が「0000」から始まれば条件を満たすということになります。
ナンス値の検証
def validNonce(lastNonce, nonce, conditions):
candidate = f'{lastNonce}{nonce}'.encode()
# ハッシュ値を計算
candidate_hash = hashlib.sha256(candidate).hexdigest()
# 計算したハッシュ値が条件を満たすかどうか?
return candidate_hash.startswith(conditions)
ナンス値の検証関数です。ハッシュ値の先頭が指定した値で始まるかどうかを確認しています。
ハッシュ値の先頭が「0000」になるナンス値を見つけてブロックを追加するようにして実行してみます。
# 初期ブロック追加
blockChain.createBlock(nonce = 100, preHash = '0' * 64)
# ナンス値探索
nonce = blockChain.findNonce(blockChain.chain[-1]['nonce'], '0000')
# ナンス値が見つかったのでブロックを追加
blockChain.createBlock(nonce)
かかった時間は平均0.027秒でした。
proof関数の第二引数を「'00000'」にして、ハッシュ値の先頭が「00000」になるナンス値を見つけるようにした場合にかかる時間は平均0.7秒でした。
一桁増えるだけで処理時間はおよそ26倍となりました。
現実のブロックチェーンの計算の難易度はこれより圧倒的に高いため計算には巨大なデータセンターが必要になります。
ブロックチェーンが改ざんに強い理由
ブロックチェーンの特徴の一つに「改ざんが極めて困難」という性質があります。
これは以下の2つの仕組みによるものです。
① 各ブロックが前のブロックのハッシュ値を参照している
上記コードではブロックがpre_hashというキー名で前のブロックのハッシュ値を持つようにしています。
例えばブロック2の中身を改ざんした場合、当然ブロック2のハッシュ値も変わってしまいます。
そうなるとブロック3が持つブロック2のハッシュ値と一致しなくなるので改ざんが検出できます。
➁ PoW(ナンス値)を再計算しないと整合しない
ブロックの中身を改ざんしてハッシュ値が変わるとブロックが持つナンス値がPoWの条件を満たさなくなります。
そして後続するブロックが持つ「前のブロックのハッシュ値」も変わるので、結局は後続するすべてのブロックのナンス値を再計算しないといけなくなります。
先述のとおり、ナンス値の計算には巨大なマシンパワーが必要になるので、再計算は現実的には不可能となります。
まとめ
ブロックチェーンを簡易的に実装してみることで
- ブロック同士をハッシュでつなぐ意味
- PoW(ナンス値探索)の重要性
- 改ざんが不可能に近い理由 といったことを理解することができました。
Webでよく聞く技術キーワードでも実際に手を動かして実装してみることで理解が一気に深まるのでおすすめです。

