ブロックチェーンのデータ構造
なぜ台帳は一度書いたら書き換えられないのか。ブロックを前ブロックハッシュで鎖状に連結し、内部をマークル木で束ねる仕組みを原理から追えば、改ざん検知が「後から気づく」ではなく「構造上ばれる」理由が腑に落ちる。
- 1.各ブロックはヘッダに前ブロックのハッシュを含む。これでブロックが時系列に連結され、途中の1ビットを変えると以降すべてのハッシュがずれて連鎖が壊れる。改ざんは隠せず必ず検出できる。
- 2.ブロック内の全トランザクションはマークル木で1個のマークルルートに畳まれ、ヘッダに入る。ヘッダのハッシュだけで本文全体の完全性が固定され、包含証明も O(log N) で済む。
- 3.台帳は上書き・削除をしない追記専用(append-only)構造。状態は「最新スナップショット」ではなく「創世ブロックからの全履歴」として保持され、各時点の状態は履歴の再生で導かれる。
台帳を「鎖」として捉える
ブロックチェーンは、名前のとおりブロックを鎖状につないだデータ構造です。要点は、ブロックそのものよりつなぎ方にあります。データベース(/database/)の行を後から UPDATE できるのとは対照的に、ブロックチェーンは一度確定したデータを構造的に書き換え不能にします。しかも「監査ログを別途取って後で照合する」のではなく、改ざんがあればハッシュ値の不整合として即座に露見するよう、データ構造自体に検知能力が埋め込まれています。
本記事では、(1) 前ブロックハッシュによる連結、(2) 連結がなぜ改ざんを検知できるか、(3) ブロック内部をまとめるマークルルートの役割、(4) 全体を貫く追記専用(append-only)という設計思想、の順に原理を追います。合意形成(誰がブロックを追加してよいか)は/devops/の合意アルゴリズムの領分なので、ここではデータ構造そのものに焦点を絞ります。
以下の議論は、SHA-256 のような暗号学的ハッシュ関数 H の3性質に全面的に依存します。(1) 一方向性(原像計算困難)、(2) 第二原像困難性(ある入力と同じハッシュになる別入力を作れない)、(3) 衝突困難性。とくに雪崩効果——入力を1ビット変えると出力の約半分のビットが無相関に変わる——が、後述の「1バイトの改ざんでハッシュが総取り替えになる」性質の源泉です。ハッシュ関数の内部構造は /security/ を参照。
ブロックの構造とヘッダ
1つのブロックは大きくヘッダと**本文(トランザクション群)**に分かれます。連結を担うのはヘッダです。ビットコインを例に、ヘッダに含まれる代表的なフィールドを挙げます。
| フィールド | 役割 | 連結・検証との関係 |
|---|---|---|
| 前ブロックハッシュ | 1つ前のブロックヘッダのハッシュ | 鎖を後ろ向きにつなぐ。改ざん検知の核 |
| マークルルート | 本文の全トランザクションを畳んだ根ハッシュ | 本文の完全性をヘッダ側に固定する |
| タイムスタンプ | ブロック生成時刻 | 時系列の記録 |
| nonce / 難易度 | PoWで解を探す可変値と目標値 | 合意・採掘に使う(構造とは別レイヤ) |
重要なのは、ブロックのIDそのものがヘッダのハッシュである点です。ブロックハッシュは H(ヘッダ) として計算され、ヘッダには前ブロックハッシュとマークルルートが含まれます。つまりブロックハッシュは、**そのブロックの本文(マークルルート経由)と、それ以前の全履歴(前ブロックハッシュ経由)**の両方を要約した値になります。
BlockHash(n) = H( Header(n) )
Header(n) = { prevHash: BlockHash(n-1),
merkleRoot: MerkleRoot(本文のTx群),
timestamp, nonce, ... }
(上の { ... } はヘッダの構成要素を示す擬似表記です。)
前ブロックハッシュによる連結
各ブロックのヘッダは、直前のブロックヘッダのハッシュを prevHash として保持します。これにより、ブロックは新しい方から古い方へ一方向にたどれる逆向きの単方向リストを形成します。先頭は特別な**創世ブロック(genesis block)**で、prevHash を持たない(またはゼロで固定された)起点です。
[Genesis] ← [Block1] ← [Block2] ← ... ← [Block n]
↑ prevHash=0 ↑ prevHash ↑ prevHash=H(Header n-1)
ここで矢印は prevHash が指し示す向きです。ポインタが値ではなく**ハッシュ(内容そのものの要約)**である点が普通の連結リストと決定的に違います。通常のポインタは「どこにあるか(アドレス)」しか示しませんが、ハッシュポインタは「どこにあり、かつ中身が何であるべきか」を同時に固定します。参照先の中身が1ビットでも変われば、ハッシュ値が変わり、参照が食い違うのです。
改ざんが構造的に露見する理由
いま攻撃者が過去の Block k の本文(あるトランザクションの金額など)を書き換えたいとします。連鎖して何が起きるかを追います。
| 段階 | 起きること | 帰結 |
|---|---|---|
| 1. 本文を改変 | Tx が1つ変わる → マークルルートが変わる | Header(k) の中身が変わる |
| 2. ヘッダが変質 | Header(k) が変わる → BlockHash(k) が変わる | Block k のIDが別物になる |
| 3. 次ブロックと不整合 | Block(k+1) の prevHash は旧 BlockHash(k) のまま | prevHash ≠ H(Header k) となり鎖が切れる |
| 4. 連鎖崩壊 | 整合を取るには k+1 以降を全て作り直す必要 | 末尾まで全ブロックの再計算を強制される |
つまり、たった1バイトの改ざんがその地点から末尾まで全ブロックのハッシュを無効化します。雪崩効果により部分修正では辻褄が合わず、Block k を書き換えた者は k から最新ブロックまでをすべて再構築しない限り、正当な鎖を提示できません。
データ構造それ自体が保証するのは**「改ざんは必ず検出できる(隠せない)」という完全性です。「改ざんを実行できない」ことまでは、構造だけでは保証しません。PoW型では、k 以降を作り直すには各ブロックの難易度パズルを解き直し、しかも正直な鎖の伸長速度を上回って追い越す必要があります。これが事実上の不可逆性(実務上の確定)を与えます。逆に言えば、検証者が正しい最新ハッシュ(またはヘッダ列)**を独立に知っていることが検知の前提です。ヘッダさえ正しければ、末尾のハッシュ1本から鎖全体の完全性を再確認できます。
この「末尾の1ハッシュが全履歴を封印する」性質は、ハッシュ関数を連結して過去を固定するハッシュチェーンそのものであり、追記専用ログの改ざん検知(タイムスタンプ、監査ログ)と同じ原理です。
マークルルートの役割
ブロック本文には数百〜数千のトランザクションが入りえます。これらをそのまま連結してハッシュしてもヘッダに1個の要約は作れますが、それでは「特定のトランザクションがこのブロックに含まれる」ことを、本文全体を持たずに検証することができません。ここで**マークル木(ハッシュ木)**を使います。
各トランザクションのハッシュを葉に置き、隣り合う2つを連結してハッシュする操作を、ノードが1つ(=マークルルート)になるまで繰り返します。この根だけをヘッダに入れます。
H = ハッシュ関数
葉: L0=H(Tx0) L1=H(Tx1) L2=H(Tx2) L3=H(Tx3)
中間: N01=H(L0 || L1) N23=H(L2 || L3)
根: MerkleRoot = H(N01 || N23)
マークルルートをヘッダに含める効果は2つです。
| 効果 | 内容 | 利点 |
|---|---|---|
| 本文完全性の固定 | Tx が1つでも変わると根が変わりヘッダも変質 | 本文改変は前節の連鎖崩壊に必ず波及する |
| 軽量な包含証明 | 根までの経路上の兄弟ハッシュだけで再計算できる | 全 N 件でなく O(log N) 個の証明で検証可(SPV) |
たとえば Tx0 がブロックに含まれることは、L1 と N23 の2つの兄弟ハッシュ(authentication path)だけ渡せば、検証者が H(H(L0 || L1) || N23) を計算してヘッダ内のマークルルートと突き合わせることで確認できます。全トランザクションをダウンロードせずヘッダだけ持つ**軽量クライアント(SPV, Simplified Payment Verification)**が成立するのは、この O(log N) 証明のおかげです。
マークル木の実装には既知の危険があります。(1) 葉と内部ノードを同じ形式でハッシュすると、内部ノードのハッシュ値を「葉のデータ」と偽り、別の木で同じ根を作れる第二原像攻撃が成立します。対策は葉と内部ノードに異なるプレフィックス(例 0x00 / 0x01)を付けること。(2) 葉数が奇数のとき最後の葉を複製して埋める方式は、異なるトランザクション集合が同一の根を生む延性を招きます(ビットコイン初期の CVE-2012-2459)。マークルルートの詳細と認証データ構造一般は /security/ を参照。
追記専用(append-only)という思想
以上を貫くのは、台帳が追記専用である、という設計思想です。既存ブロックの上書きも削除も行わず、常に末尾へ新しいブロックを足すだけ。前ブロックハッシュとマークルルートは、この一方向性を暗号で強制する装置です。
この帰結として、ブロックチェーンが保持するのは「口座の現在残高」のような最新スナップショットではなく、創世ブロックからの全トランザクション履歴です。ある時点の状態は、履歴を先頭から順に適用(再生)することで導かれます。
| 観点 | 従来DBの更新モデル | ブロックチェーンの追記モデル |
|---|---|---|
| 書き込み | 既存レコードを UPDATE / DELETE | 末尾に追記のみ(過去は不変) |
| 保持する対象 | 現在の状態(最新値) | 全履歴。状態は再生で導出 |
| 改ざん耐性 | アクセス制御・監査ログに依存 | ハッシュ連鎖で構造的に検知 |
| 近い発想 | 上書き型テーブル | 追記専用ログ / イベントソーシング |
この構造は目新しい発明というより、追記専用ログ(データベースの WAL やイベントソーシング)にハッシュチェーンによる改ざん検知と分散合意を組み合わせたものと見ると位置づけやすくなります。上書きを捨てて全履歴を残す代償に、過去を書き換えれば必ずばれるという強い完全性を得ているわけです。
・ブロックハッシュ=H(ヘッダ)。ヘッダは前ブロックハッシュとマークルルートを含むため、1本のブロックハッシュが「本文+全履歴」を封印する。
・改ざんはその地点以降の全ハッシュを無効化する。ゆえに部分修正は不可能で、末尾まで作り直しを強制される(=検知は構造で保証)。
・マークルルートは本文完全性の固定と、O(log N) の包含証明(SPV)の両方を担う。葉・内部ノードのプレフィックス分離は実装必須。
・台帳はappend-only。保持するのは最新残高ではなく全履歴で、状態は再生で得る。
まとめ
- ブロックチェーンは、各ヘッダに前ブロックハッシュを持たせて連結したハッシュチェーン。ブロックのIDはヘッダのハッシュ自体で、本文と全履歴の両方を要約する。
- 過去ブロックを1バイト改ざんすると、マークルルート→ブロックハッシュ→次ブロックの
prevHash不整合と波及し、その地点から末尾まで全ブロックのハッシュが無効化される。だから改ざんは隠せず、構造的に検知される。 - マークルルートは本文全トランザクションを1ハッシュへ畳み、ヘッダに固定する。本文完全性の担保に加え、**O(log N) の包含証明(SPV)**を可能にする。実装では葉と内部ノードのプレフィックス分離が必須。
- 全体を貫くのは**追記専用(append-only)**の思想。上書き・削除をせず全履歴を残し、各時点の状態は履歴の再生で導く。過去を書き換えれば必ずばれる、という完全性が本質。
ブロックチェーン Article
ブロックチェーンのデータ構造を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
ブロックチェーン
比較で見る軸
難易度: advanced / カテゴリ: ブロックチェーン / タグ数: 6
導入後に効く点
ブロック内の全トランザクションはマークル木で1個のマークルルートに畳まれ、ヘッダに入る。ヘッダのハッシュだけで本文全体の完全性が固定され、包含証明も O(log N) で済む。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- ブロックチェーン
- タグ数
- 6
判断チェックリスト
- 自社の用途が「ブロックチェーン / 分散台帳」に近いか確認する。
- 強みである「各ブロックはヘッダに前ブロックのハッシュを含む。これでブロックが時系列に連結され、途中の1ビットを変えると以降すべてのハッシュがずれて連鎖が壊れる。改ざんは隠せず必ず検出できる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。