ブロックチェーンの「ブロック」と「チェーン」って結局なに?ハッシュで図解する
はじめに
「ブロックチェーンを学習していて、結局なぜ改ざんできないのか?」というのが、少し前までの自分の状態でした。仮想通貨やNFTといった文脈では耳にするものの、「データが繋がっているとなぜ改ざんに強いのか」 という核心部分の理解が不十分でした。
そこで本記事では、ブロックチェーンの土台となる仕組みを「ハッシュ → ブロック → チェーン」の3段階で整理します。
この記事を読むとわかること:
- ハッシュ関数とは何か、なぜブロックチェーンに必須なのか
- 1つのブロックの中身と、それらをどう繋いでチェーンにするのか
- 「改ざんしようとするとなぜ全部が壊れるのか」を自分の言葉で説明できる
なお、本記事では PoW(Proof of Work)やマイニング、分散ネットワークには踏み込みません。まずは「データ構造」としてのブロックチェーンに集中します。
そもそもハッシュ関数とは
ブロックチェーンを理解するには、まず ハッシュ関数 という道具を知る必要があります。
ハッシュ関数とは「どんな長さのデータを入れても、決まった長さの文字列を返す関数」です。返ってくる文字列のことを ハッシュ値 と呼びます。
📝 混同しやすいので整理しておきます。
- ハッシュ関数: 入力を変換する「装置」そのもの(例: SHA-256)
- ハッシュ値: その装置に入力を通したときに出てくる「結果の文字列」
たとえば SHA-256 というハッシュ関数に "Hello" を入れると、以下のような64文字の文字列(ハッシュ値)が得られます。
185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
このハッシュ関数には、ブロックチェーンに使われるうえで重要な 3つの性質 があります。
性質1: 同じ入力なら必ず同じ出力
"Hello" を SHA-256 に入れれば、いつ、どの環境で実行しても、同じハッシュ値が返ります。これは当たり前のようでいて、「データが書き換えられていないことの確認」 に使える重要な性質です。
性質2: 入力を1文字変えるだけで出力が全く違うものになる(雪崩効果)
"Hello" の最後の "o" を "p" に変えて "Hellp" にしただけで、ハッシュ値はまったく別の値になります。
図1: 「Hello」と「Hellp」のハッシュ値比較(雪崩効果)
この性質を 雪崩効果(avalanche effect) と言います。「わずかな改変で検知を免れる」ことが原理的にできない、というのがポイントです。
性質3: 出力から入力は逆算できない(一方通行)
ハッシュ値から元の入力を割り出すことは、現実的な計算量ではできません。これを 一方向性 と呼びます。
つまり「ハッシュ値だけを見せても、入力は復元できない」ということです。
Pythonでミニ実験
文章だけでは具体的なイメージが湧きにくいので、実際にコードで挙動を確認します。Python の標準ライブラリ hashlib で試せます。
import hashlib
def sha256(text: str) -> str:
return hashlib.sha256(text.encode()).hexdigest()
print(sha256("Hello"))
print(sha256("Hellp")) # 1文字違い
print(sha256("Hello")) # もう一度同じ入力
実行結果:
185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
2113cd4a7aebc3a400a234c3de8a5c0c4d9fa8c6ca370d030aac9ab51e8c4476
185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969ここから読み取れるのは、
- 1行目と2行目はまったく別の文字列になっている(性質2: 雪崩効果)
64文字中60文字が違う結果になりました(ティール色の4文字d840だけが偶然一致)。1文字の変更で9割以上が別物になっています。 - 1行目と3行目は完全に一致している(性質1: 決定的)
わずか3行ですが、ハッシュ関数の挙動を確認するには十分です。
ブロックの中身を見てみる
ハッシュ関数がわかったところで、次は「ブロック」の話です。
ブロックチェーンにおける ブロック は、概念的には「3つの欄を持った箱」と捉えるのが分かりやすいです。
- データ欄: 記録したい情報(送金履歴、メモなど)
- 前のハッシュ欄: 1つ前のブロックのハッシュ値
- 自分のハッシュ欄: 「データ欄 + 前のハッシュ欄」をまとめてハッシュ関数に通した結果
図2: 1個のブロックの中身
ここで重要なのは、「自分のハッシュ欄」がデータ欄と前のハッシュ欄の両方から計算されるという点です。
つまり、
- データ欄を書き換えれば → 自分のハッシュ値も変わる
- 前のハッシュ欄を書き換えれば → 自分のハッシュ値も変わる
という構造になっています。雪崩効果により、わずかな変更でも別のハッシュ値が出力されます。
ブロックをつなげるとどうなる?
1個のブロックの中身がわかったので、次はそれを チェーン状に繋げる 仕組みを見ていきます。
繋ぎ方は単純で、「次のブロックの『前のハッシュ欄』に、前のブロックの『自分のハッシュ欄』をコピーしてくる」 だけです。
図3: 3個のブロックが繋がっている正常なチェーン
最初のブロック「ブロック0」(ジェネシスブロック と呼ばれます)には、繋ぐ前のブロックが存在しません。そのため「前のハッシュ欄」には、通常 "0" などの決まった値を入れておきます。
この時点では、まだ「ただのリンク付きデータの列」にしか見えないかもしれません。改ざん耐性が本当の意味で効いてくるのは、次のセクションです。
もし途中のブロックを改ざんしたら?
本記事の核心となる「なぜブロックチェーンは改ざんに強いのか」を扱います。
たとえば「ブロック1のデータを後から書き換えたい」という攻撃者を想定します。通常のデータベースであれば、該当行を上書きするだけで完了します。ブロックチェーンではどうなるのか、順を追って見ていきます。
ステップ1: ブロック1のデータ欄を書き換える
ブロック1のデータ欄だけを書き換えた状態を考えます。
このとき、ブロック1の「自分のハッシュ欄」に保存されている値は、書き換え前のデータから計算された古いハッシュのままです。
一方、検証する側がブロック1のデータから改めてハッシュを計算すると、雪崩効果で全く別の値になります。
→ 「保存されているハッシュ」と「今計算したハッシュ」がブロック1の中で食い違う = 不一致が発生 します。
(コードでは チェック1 で検出されるパターンです)
ステップ2: 攻撃者がブロック1のハッシュも再計算する
「ブロック1の『自分のハッシュ欄』も新しいハッシュ値で上書きすればよい」と考えるかもしれません。
これによりブロック1の中身は内部的に整合します(チェック1はパスする)。
しかし、ブロック2の「前のハッシュ欄」は 書き換え前の古いハッシュ値 のままです。
→ ブロック1の新しい hash と、ブロック2の prev が一致しない = 不一致が発生 します。
(コードでは チェック2 で検出されるパターンです)
ステップ3: ブロック2の「前のハッシュ欄」も書き換える
「ブロック2の『前のハッシュ欄』もブロック1の新しい hash に合わせて書き換えればよい」と考えるかもしれません。
しかし今度は、ブロック2の 内部の整合性 が崩れます。ブロック2の「自分のハッシュ欄」は元々「古い prev + data」から計算された値なので、prev を書き換えた瞬間、再計算結果と一致しなくなります(チェック1で検出)。
→ 攻撃者は ブロック2の「自分のハッシュ欄」も再計算 する必要があります。
→ しかしブロック2の hash が変われば、今度はブロック3の「前のハッシュ欄」と不一致になります(チェック2で検出)……。
この連鎖がチェーンの末尾まで続きます。
図4-A: 改ざん前(正常)
図4-B: 改ざん後(ブロック1の data と hash を書き換えた)
ブロック1の hash を 9e9d27c0... に書き換えても、ブロック2の prev は古い 674ceded... のまま → リンクが切れて検証で不一致が発覚します。
つまり、ブロック1を1個書き換えるためには、それ以降のすべてのブロックを連鎖的に書き換えないとバレる、という構造になっています。
ステップ4: 「末尾まで全部書き換える」とどれくらい大変か?
より長いチェーンを想定してみます。
仮にチェーンが100ブロックあり(ここでは先頭からブロック1, 2, ..., 100 と数えます)、50番目のブロックの内容を後から書き換えたいとします。これまでのステップから分かるように:
- ブロック50:
dataとhashを再計算(改ざん対象) - ブロック51: ブロック50の hash が変わったので
prevとhashを再計算 - ブロック52: 同じく
prevとhashを再計算 - ...
- ブロック100: 同じく
prevとhashを再計算
つまり、改ざん対象から末尾までの全 51 ブロックを書き換えない限り、検証でどこかで必ず不一致が出ます。
図5: 改ざんは末尾まで連鎖する
ここまでであれば、計算機の力で押し切れる可能性があります。
ただし、もし**「1ブロックを作るのに何分もかかる」**仕組みが入っていたら、どうでしょうか。
たとえば1ブロックあたり10分かかるなら、51ブロックの再計算には 510分(約8.5時間) かかります。その間にも正規のチェーンには新しいブロックがどんどん追加されていくので、攻撃者は 永遠に追いつけません。
実際のブロックチェーンには、まさにこの「1ブロック作るのを意図的に重くする」仕組みが入っており、改ざんを「理論的には可能だが現実的には不可能」な計算コストに押し上げています。
これが「データ構造としてのブロックチェーン」が改ざんに強い理由の核心です。
(実運用ではここに、「みんなで監視する分散ネットワーク」が加わることで、より強固になります。)
Python によるミニ実装
ここまでの内容を、最小構成のブロックチェーンとして Python で実装します。
ブロックの定義
import hashlib
class Block:
def __init__(self, data: str, previous_hash: str):
self.data = data
self.previous_hash = previous_hash
self.hash = self.calc_hash()
def calc_hash(self) -> str:
# データ + 前のハッシュ をまとめてSHA-256でハッシュ化
body = self.data + self.previous_hash
return hashlib.sha256(body.encode()).hexdigest()
Block クラスは「データ欄」「前のハッシュ欄」「自分のハッシュ欄」の3つを持つシンプルな構造です。
チェーンの検証関数
def is_chain_valid(chain: list[Block]) -> bool:
for i, block in enumerate(chain):
# チェック1: そのブロックのハッシュは、いま計算しても同じか?
if block.hash != block.calc_hash():
print(f"❌ ブロック{i}: データかハッシュが書き換えられています")
return False
# チェック2: 前のブロックとリンクが繋がっているか?
if i > 0 and block.previous_hash != chain[i - 1].hash:
print(f"❌ ブロック{i}: 前のブロックとのリンクが切れています")
return False
print("✅ チェーンは健全です")
return True
検証は2種類あります。「ハッシュの整合性」 と 「前のブロックとのリンク」 の両方を確認しています。
3つのシナリオで動かしてみる
# --- シナリオ1: 正常な状態 ---
genesis = Block("最初の取引", previous_hash="0")
b1 = Block("AがBに100円送った", previous_hash=genesis.hash)
b2 = Block("BがCに50円送った", previous_hash=b1.hash)
chain = [genesis, b1, b2]
is_chain_valid(chain)
# → ✅ チェーンは健全です
# --- シナリオ2: ブロック1のデータだけ書き換える ---
chain[1].data = "AがBに10000円送った" # 改ざん
is_chain_valid(chain)
# → ❌ ブロック1: データかハッシュが書き換えられています
# (チェック1で検出: 「自分のハッシュ」と再計算が一致しない)
# --- シナリオ3: 攻撃者が賢く、ブロック1のハッシュも再計算 ---
chain[1].hash = chain[1].calc_hash() # 自分のハッシュも辻褄合わせ
is_chain_valid(chain)
# → ❌ ブロック2: 前のブロックとのリンクが切れています
# (チェック2で検出: ブロック2の previous_hash が古いまま)
実行結果:
=== シナリオ1: 正常な状態 ===
ブロック0: data='最初の取引' prev=0... hash=93c83e4b...
ブロック1: data='AがBに100円送った' prev=93c83e4b... hash=674ceded...
ブロック2: data='BがCに50円送った' prev=674ceded... hash=d418d642...
✅ チェーンは健全です
=== シナリオ2: ブロック1のデータだけ書き換える ===
ブロック1のデータを 'AがBに10000円送った' に改ざん
保存されているハッシュ : 674ceded...
再計算したハッシュ : 9e9d27c0... ← 食い違う!
❌ ブロック1: データかハッシュが書き換えられています
=== シナリオ3: ブロック1のハッシュも再計算してつじつまを合わせたら? ===
ブロック1: data='AがBに10000円送った' hash=9e9d27c0...
ブロック2の『前のハッシュ』: 674ceded... ← まだ古いまま!
❌ ブロック2: 前のブロックとのリンクが切れています
シナリオ2の 「保存されているハッシュ 674ceded... と再計算したハッシュ 9e9d27c0... が食い違う」 が、前セクションのステップ1で説明した「ブロック内部の不一致」そのものです。
シナリオ3では、ブロック1の hash を 9e9d27c0... に書き換えたものの、ブロック2の previous_hash がまだ古い 674ceded... のままで、リンクが切れている様子が確認できます。
重要なのはシナリオ3です。
「自分のハッシュ」の辻褄を合わせても、次のブロックの『前のハッシュ欄』が古いままなので検知される、という点です。
完全に検知を免れるためには、ブロック1以降のすべてのブロックを書き換える必要があります。
実際のブロックチェーンでは、「全部を書き換えるのは現実的に不可能」になるよう、PoW などの仕組みでさらに強化されています。
ミニ実装と現実のブロックチェーンの違い
ここまでで「ハッシュで繋がるデータ構造としてのブロックチェーン」の核となる部分は押さえました。
ただし、現実の Bitcoin やイーサリアムのブロックチェーンには、もう少し多くの要素が含まれています。
本記事のミニ実装と現実の差を整理しておきます。
ブロックの中身: ミニ実装 vs Bitcoin
| 項目 | 本記事のミニ実装 | 現実の Bitcoin |
|---|---|---|
data |
文字列1つ(例: "AがBに100円送った") | 複数のトランザクションを束ねたもの |
previous_hash |
前ブロックのハッシュ | 同じ |
hash |
data + previous_hash の SHA-256 | data + previous_hash + nonce 等 を SHA-256 で2回 |
nonce |
なし | あり(「条件を満たすハッシュ」を探すための調整値) |
timestamp |
なし | あり(ブロックの生成時刻) |
merkle_root |
なし | あり(複数トランザクションを効率的に検証するための小さなハッシュ値) |
| ハッシュ関数 | SHA-256(1回) | SHA-256(2回。SHA-256d と呼ばれる) |
それでも本質は同じ
これらの差はありますが、「ハッシュで前後を繋いで改ざんを検知する」というコア構造はまったく同じです。
つまり本記事のミニ実装は、Bitcoin の機能を削った最小版になっており、
-
nonceを加えれば → PoW(採掘)が表現できる -
merkle_rootを加えれば → 1ブロックで大量のトランザクションが扱える -
timestampを加えれば → 時系列の整合性が取れる - 全ノードで同じチェーンを持ち合えば → 分散ネットワーク
機能を1つずつ加えていくと現実のブロックチェーンに近づきます。
本記事の最小実装は、いわば「ブロックチェーンの 原子核」に相当するものです。
ここまでで触れなかったこと
本記事は「データ構造としてのブロックチェーン」に絞ったため、以下の話題は意図的に省きました。
- PoW(Proof of Work): 「ブロックを作るのに膨大な計算が必要」という仕組み。改ざんに必要なコストを跳ね上げる役割
- マイニング: PoWを実際に解いて新しいブロックを追加していく作業
- 分散ネットワーク / P2P: 1台のサーバーに頼らず、世界中のノードが同じチェーンを持ち合うことで耐改ざん性をさらに上げる仕組み
- コンセンサスアルゴリズム: 「どのブロックが正しい次の1個か」をネットワーク全体で合意する方法(PoW, PoS など)
これらは「ハッシュで繋がっているだけ」の構造の上に乗る、もう一段上のレイヤーの話となります。
まとめ
ここまでの内容をまとめると、
- ハッシュ関数 は、入力を固定長の文字列に変換する装置。同じ入力なら同じ出力、入力を少しでも変えると出力は激変、出力から入力は逆算できない、の3点が肝
- ブロック は「データ + 前のハッシュ + 自分のハッシュ」の3欄を持つ構造
- チェーン は、次のブロックの「前のハッシュ欄」に前のブロックのハッシュをコピーすることで作られる
- 改ざん耐性 は、「途中のブロックを書き換えると、その後ろのハッシュが全部ずれるので、結局チェーン全部を書き換えないとバレない」という性質から生まれる
「改ざんできない魔法」ではなく、「改ざんしようとすると芋づる式に壊れるデータ構造」という見方をすると、イメージしやすくなります。
最後までお読みいただきありがとうございました。