Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

ブロックチェーンのデータ構造

ブロックチェーンのデータ構造 1枚まとめ

暗号学的ハッシュ関数

ブロックチェーンのデータ構造はほぼすべて 暗号学的ハッシュ関数(cryptographic hash function) の性質に依存している。

ハッシュ関数 HH は任意長の入力を固定長の出力(ハッシュ値、ダイジェスト)に写す関数。暗号学的ハッシュ関数は次の性質を満たす。

性質内容
原像計算困難性(preimage resistance)ハッシュ値 yy から H(x)=yH(x) = y となる xx を求めるのが困難
第二原像計算困難性(second preimage resistance)xx が与えられたとき、H(x′)=H(x)H(x') = H(x) となる別の x′≠xx' \neq x を求めるのが困難
衝突困難性(collision resistance)H(x)=H(x′)H(x) = H(x') となる任意の組 x≠x′x \neq x' を求めるのが困難

加えて、入力が1ビットでも変わると出力が予測不能に大きく変わる(雪崩効果)。

BitcoinではSHA-256(多くの箇所で2回適用する SHA256(SHA256(x)))、EthereumではKeccak-256が使われる。

あいうえお fdb481ea956fdb654afcc327cff9b626966b2abdabc3f3e6dbcb1667a888ed9a
あいうえこ 59d9b0d8d3e1262bd220ebc58989569c064b48df22c28dfcc3fd2186b484af34
あいうおえ a762518a8b8eff0175e1e0e9493594db95b23aba9f04dae7f52621c1dafddea9

入力の長さにかかわらず出力は256bit(16進数で64桁)になるので、大きなデータの「要約」としても使える。

ハッシュチェーン

各ブロックは ブロックヘッダー に直前のブロックのハッシュ値(previous block hash)を持つ。これにより、ブロックが鎖状に連結される。

あるブロックの中身を改ざんすると、そのブロックのハッシュ値が変わり、次のブロックが持つ prev hash と一致しなくなる。整合性を保つには以降のすべてのブロックを作り直す必要がある。後述のProof of Workと組み合わせると、ブロックの作り直しには莫大な計算コストがかかるため、過去の記録の改ざんが事実上不可能になる。

なお、各ブロックヘッダーには自分自身のハッシュ値は入っていない(自己言及になるため)。

以下は単純なハッシュチェーンの実装例。

改ざん前: True
改ざん後: False

マークルツリー

ブロック内の全トランザクションの要約は マークルツリー(Merkle tree) で作る。トランザクションのハッシュ値(txid)を葉として、隣り合う2つを連結してハッシュ化する操作をトーナメント表のように繰り返し、最後に残った1つの値を マークルルート(Merkle root) と呼ぶ。

  • ブロックヘッダーにはマークルルート(32バイト)だけを入れる。トランザクションが1件でも100万件でもサイズは同じ

  • どれか1つのトランザクションの内容や順番が変わると、マークルルートがまったく別の値になる

  • Bitcoinでは、葉が奇数個のときは最後のハッシュ値を複製してペアを作る

マークルプルーフ

単にトランザクションを全部連結してハッシュ化しても要約にはなる。わざわざ木構造にするのは、ある1件のトランザクションがブロックに含まれていることを、少ないデータで証明できるからである。

トランザクション KK がブロックに含まれることを示すには、KK から根までの経路上にある「兄弟ノード」のハッシュ値だけがあればよい。これを マークルプルーフ(Merkle proof, inclusion proof) と呼ぶ。

NN 個の葉に対してプルーフのサイズは O(log⁡2N)O(\log_2 N) で、全件連結方式の O(N)O(N) に比べて非常に小さい。一般に kk 分木では必要なハッシュ数は (k−1)log⁡kN(k-1)\log_k N で、k=2k=2 のとき最小になる。

この性質は、ブロックヘッダーだけを持つ軽量ノード(SPVノード、ライトクライアント)が、フルノードから受け取ったプルーフで自分宛ての取引を検証するのに使われる。

マークルルート: 552d2da993a20ea1a79aac3570a81c10fdb40173bd9d6c0dc9e1b5ead0885d80
プルーフに必要なハッシュ数: 4 (葉の数: 16 )
tx10 の検証: True
偽の tx の検証: False

ブロックの構造(Bitcoin)

Bitcoinのブロックは、80バイトのブロックヘッダーとトランザクションのリストからなる。

サイズフィールド内容
4 bytesversionプロトコルのバージョン
32 bytesprevious block hash前ブロックのブロックハッシュ
32 bytesmerkle rootブロック内の全トランザクションのマークルルート
4 bytestimestampブロックの生成時刻(Unix時間)
4 bytesdifficulty target (bits)PoWの難易度
4 bytesnonceマイニングで探索する値

ブロックハッシュはこのヘッダーのダブルSHA-256。ヘッダーにマークルルートが含まれるので、トランザクションを1つでも改ざんすると

txidが変わる → マークルルートが変わる → ブロックハッシュが変わる → 次のブロックの prev hash と不一致

となり、すぐに検出できる。

台帳のモデル:UTXOとアカウント

ブロックに記録する「取引」の表現方法には大きく2つの流儀がある。

UTXOモデル

Bitcoinが採用する UTXO(Unspent Transaction Output)モデル では、残高という概念を直接持たない。各トランザクションは

  • 入力(input):過去のトランザクションの未使用出力(UTXO)を参照して消費する

  • 出力(output):金額と、それを使うための条件(宛先)を指定して新しいUTXOを作る

からなる。あるアドレスの残高は、そのアドレス宛のUTXOの合計として計算される。現金の紙幣・硬貨に近い。

UTXOは分割して使えないため、一部だけ支払いたいときは自分宛の「おつり」出力を作る。入力の合計と出力の合計の差額が手数料になる。

アカウントモデル

Ethereumが採用する アカウントモデル では、各アカウントの残高や状態(state)をグローバルな状態として管理し、トランザクションはその状態を遷移させる命令として扱う。銀行口座に近い。

観点UTXOモデルアカウントモデル
管理するもの取引の流れ(フロー)のみ各アカウントの状態(ストック)
残高UTXOを合計して算出状態として直接保持
並列処理異なるUTXOを使う取引は独立に検証しやすい同じアカウントに触れる取引は逐次処理が必要
プライバシー取引ごとにアドレスを変えやすい同じアドレスを使い続けがち
プログラマビリティ限定的(使用条件のスクリプトのみ)汎用的なプログラム(スマートコントラクト)と相性がよい
二重支払い防止UTXOが未使用かを確認アカウントごとのnonce(取引の通し番号)で確認
採用例Bitcoin, Cardano(拡張UTXO)Ethereum, Solana