
はじめに
本ページは設計文書シリーズのL1 #7「ストレージ設計」である。ストレージ層を扱う: Bitcoin Coreノードが検証済みデータをディスクにどう永続化するか、使用可能なコインをメモリーとディスク上でどう追跡するか、状態をどう復旧またはブートストラップするか。
トランザクション設計ページではUTXOとは何かを説明した。ブロックとチェーン設計ページではブロックがチェーンにどう連結されるかを説明した。本ページは一段階下に移る: これらの抽象概念を耐久的かつ検索可能にするディスク上およびメモリー内の構造。
4つの設問が内容を構成する:
- ブロックはどこに保存されるか? ディスク上のフラットファイル(
blk*.dat)に保存され、再編成を可能にする付随の取り消しファイル(rev*.dat)を伴う。 - UTXOセットはどこにあるか? LevelDBデータベース(コインデータベース)に置かれ、パフォーマンスのためにメモリー内にライトバックキャッシュを持つ。
- ノードはブロックをどう素早く見つけるか? ブロックハッシュをファイル位置にマッピングするLevelDBブロックインデックスを通じて。
- ノードはすべてを永久に保存することをどう回避するか? 剪定(古いブロックファイルの破棄)とassumeUTXO(信頼されたUTXOスナップショットからのブートストラップ)によって。
サトシ時代の実装(v0.1、2009年1月)と現代のBitcoin Core(v27以降を基準)で挙動が異なる箇所は、双方を記載する。
1. ストレージアーキテクチャの概要
以下の図はフルノード内部のすべての永続データストアとそれらの間のデータフローを示す。
| ストア | エンジン | 内容 | おおよそのサイズ |
|---|---|---|---|
| ブロックファイル | フラットバイナリーファイル | 生のシリアライズされたブロック、追記専用 | 約774 GB(2026年10月) |
| 取り消しファイル | フラットバイナリーファイル | 各使用済み入力の以前のコイン状態(ブロックファイルごとに1つ) | ブロックファイルとともに増える |
| コインデータベース | LevelDB | すべての未使用トランザクション出力(UTXOセット) | UTXOセットとともに増える |
| ブロックインデックス | LevelDB | ブロックヘッダーメタデータとファイル位置ポインター | ブロックファイルに比べて小さい |
| コインキャッシュ | メモリー内ハッシュマップ | コインデータベースのライトバックキャッシュ | 設定可能(既定約450 MiB、-dbcacheで指定) |
| メモリープール | メモリー内データ構造 | ブロック掲載を待つ未承認トランザクション | 設定可能(既定300 MB、-maxmempoolで指定) |
2. ブロックファイル
検証済みブロックは、データディレクトリのblocks/ サブディレクトリに保存されるblk00000.dat、blk00001.dat等の名前の一連のフラットバイナリーファイルに順次書き込まれる。各ファイルは新しいファイルが開かれるまでにおよそ128 MiBまでの生のブロックデータを保持する。
ブロックファイルのレイアウト
ファイル内の各ブロックエントリーは、4バイトのネットワークマジックナンバー(メインネット・テストネット等、ネットワークを識別する)で始まり、4バイトのリトルエンディアンサイズフィールドが続き、その後に完全なシリアライズされたブロック(ヘッダー + トランザクション + 証人データ)が格納される。v0.1の直列化境界を扱う分析は、このマジックナンバーとサイズによる枠組みを元のソースのCBlock::WriteToDiskまで遡っている。この枠組みは以来変わっていないが、Bitcoin Core 28.0からは、ブロックのディレクトリに置いた鍵で、ファイルがディスク上で既定でXORされる。
取り消しファイル (rev*.dat)
各ブロックファイルblk0NNNN.datに対して、対応するrev0NNNN.datが存在する。取り消しファイルは、対応するブロック内のすべてのトランザクションが消費したすべてのUTXOの以前の状態を保存する。このデータはチェーン再編成時のブロックの切断に不可欠である: ノードは使用されたUTXOを復元し、作成されたUTXOを除去しなければならない。
| ファイルペア | 目的 |
|---|---|
blk0NNNN.dat | 順方向データ: 受信・検証された生のブロック |
rev0NNNN.dat | 逆方向データ: ブロックのUTXOセットへの影響を取り消すために必要なコイン状態 |
取り消しデータがなければ、再編成にはジェネシスブロックからチェーン全体を再検証する必要があり、現代のハードウェアでも数時間を要する作業になる。
3. UTXOセット(コインデータベース)
UTXOセットはBitcoin Coreで最もパフォーマンスが重要なデータ構造である。すべてのトランザクション検証において、参照された入力が存在し未使用であるかの検索が必要となる。コインデータベースはchainstate/ サブディレクトリにあるLevelDBキーバリューストアにすべての未使用トランザクション出力を格納する。
UTXO検索フロー
キーバリュー構造
コインデータベースの各エントリーはアウトポイント(トランザクションハッシュ + 出力インデックス)をキーとし、コインのサトシ単位の額面、作成されたブロック高、コインベース出力かどうか、出力のロックスクリプトを保存する。
コインキャッシュ
トランザクション入力ごとにディスク上のLevelDBから読み取るのでは遅すぎる。Bitcoin Coreは読み取りを吸収し書き込みをバッチ処理するメモリー内ライトバックキャッシュ(コインキャッシュ)を維持する。ブロックが接続されると、使用済みコインはキャッシュ内で消費済みとマークされ、新しいコインが追加される。蓄積された変更は、定期的に、あるいはキャッシュがサイズ上限に達した時点で、ディスク上のLevelDBへ単一のバッチ書き込みとしてフラッシュされる。
| パラメーター | 既定値 | 効果 |
|---|---|---|
-dbcache | 450 MiB | メモリー内コインキャッシュのサイズ。値が大きいほど初期ブロックダウンロード時のディスクI/Oが減少する。 |
| フラッシュ契機 | キャッシュ満杯またはシャットダウン | ダーティエントリーがLevelDBにバッチ書き込みされる。 |
初期ブロックダウンロード(IBD)のパフォーマンス。IBD中、ノードは数十万のブロックを連続して高速に処理する。-dbcacheを大きく設定する(例: 4096 MiB以上)と、UTXOセットのより多くの部分がメモリーに保持され、ディスク読み取りとバッチ書き込み頻度が最小化されるため、IBD時間が劇的に短縮される。
4. ブロックインデックス
ブロックインデックスはblocks/index/ に保存される独立したLevelDBデータベースで、古くなったブランチのものも含め、ノードがこれまでに受信したすべてのブロックヘッダーのカタログとして機能する。各ブロックハッシュに対して、インデックスは以下を保存する:
| フィールド | 目的 |
|---|---|
| ブロックヘッダーフィールド | バージョン、前ブロックハッシュ、マークルルート、タイムスタンプ、nBits、ナンス |
| ファイル位置 | どのblk*.datファイルにこのブロックが格納されているか、どのバイトオフセットか |
| ブロック高 | チェーン内の位置 |
| チェーン仕事量 | このブロックまでの累積プルーフ・オブ・ワーク(nChainWork) |
| 検証状態 | ブロックが完全に検証済みか、ヘッダーのみ検証済みか、無効としてマークされているか |
ブロック取得パス
ブロックインデックスにより、ノードはフラットファイルを走査することなくハッシュで任意のブロックを特定できる。また、チェーン選択に必要なデータも提供する: ノードはディスクからフルブロックを読み込まずに、ブランチ先端間でnChainWork値を比較して最も仕事量の多いチェーンを判定できる。
5. メモリープールストレージ
メモリープールは、検証とポリシーチェックに合格したがまだブロックに掲載されていない未承認トランザクションのメモリー内プールである。合意形成データベースには永続化されず、各ノードが独立に維持するローカルで一時的な構造である。
メモリープールのライフサイクル
ネットワークまたはウォレットから到着したトランザクションは、まずポリシー検査と合意検証を通過してからメモリープールに受理される。どちらかの検査に失敗すれば、受理される前に破棄される。
| 特性 | 値(v27以降基準) |
|---|---|
| 最大サイズ | 300 MB(-maxmempoolで設定可能) |
| 除去ポリシー | プールが容量に達すると最低手数料率のトランザクションから除去 |
| 期限切れ | 336時間(約2週間)を超えたトランザクションを除去 |
| 永続化 | 正常シャットダウン時にmempool.datに保存; 次回起動時に読み込み |
| 手数料置換 | 完全RBFが既定のメモリープールポリシー(v28.0以降) |
メモリープールと合意形成。メモリープールはポリシーレベルの構造であり、合意形成レベルのものではない。2つのノードが全く異なるメモリープールを持っていても、どのブロックが有効かについては合意できる。トランザクションがメモリープールに存在しても、ブロックへの掲載が保証されるわけではない。意味するのは、ローカルノードがそれを有効と見なし、中継する意思があることだけである。
6. 剪定
フルアーカイブノードはこれまでに生成されたすべてのブロックを保存しており、2026年10月に約774 GBで、その前の2年はそれぞれ約80 GBずつ増えた。剪定により、ノードは古いブロックファイルと取り消しファイルを破棄しつつ、完全なUTXOセットと新規ブロック検証能力を保持できる。
| 側面 | アーカイブノード | 剪定ノード |
|---|---|---|
| ブロックデータ | ジェネシスからチップまでの全ブロック | 最新のN MiBのみ(最小550 MiB) |
| UTXOセット | 完全 | 完全(アーカイブと同一) |
| ブロックインデックス | 完全(全ヘッダー) | 完全(全ヘッダー) |
| 新規ブロック検証 | 完全 | 完全(アーカイブと同一) |
| 過去のブロック提供 | 可能 — ピアに任意のブロックを提供可 | 不可 — 保持ウィンドウ内のブロックのみ提供可 |
| ディスク使用量 | ブロックだけで約774 GB(2026年10月) | 最小約10 GB |
要点。剪定ノードはフル検証ノードである。アーカイブノードとまったく同じように、すべての新規ブロックにすべての合意規則を適用する。唯一失われる能力は、初期ブロックダウンロードを行うピアに古いブロックを提供することである。剪定は -prune=<MiB>(最小550)で設定する。
7. assumeUTXO
ジェネシスブロックから現在のチップまですべてのブロックを検証する初期ブロックダウンロード(IBD)は、ハードウェアとネットワーク速度に応じて数時間から1日以上かかる。assumeUTXO(loadtxoutset RPCはv26.0で導入; メインネットのスナップショットパラメーター(高さ840,000)はv28.0で追加)は代替のブートストラップパスを提供する: ノードは事前計算されたUTXOスナップショットを読み込み、スナップショット高から即座に新規ブロックの検証を開始し、バックグラウンドで完全な過去のチェーンを検証する。
assumeUTXOのブートストラッププロセス
信頼モデル
| フェーズ | 検証レベル | 信頼の前提 |
|---|---|---|
| スナップショット読み込み済み、バックグラウンドIBD未完了 | 新規ブロックはスナップショットUTXOセットに対して完全に検証される | ノードはハードコードされたスナップショットハッシュ(Bitcoin Core開発者がバイナリーにコンパイルしたもの)を信頼する。スナップショットが悪意あるものであった場合、バックグラウンド検証が不一致を検出するまで、ノードは無効なトランザクションを受け入れる可能性がある。 |
| バックグラウンドIBD完了、ハッシュ一致 | ジェネシスからチップまですべてのブロックの完全な検証 | 追加の信頼なし — ノードはチェーン履歴全体を独立に検証しており、従来のIBDノードと同一。 |
assumeUTXOは合意規則を変更しない。検証が行われる順序を変更する: 新規ブロックが最初(即座に有用)、過去のブロックが二番目(バックグラウンド検証)。最終状態は従来のIBDを実行したノードと同一である。
8. 二つの時代の比較
| 機能 | サトシ時代(v0.1、2009年1月) | 現代のBitcoin Core、v27以降基準 |
|---|---|---|
| 主要データベース | Berkeley DB (BDB) がトランザクションインデックスとブロックインデックスを管理; 生のブロックデータはv0.1の時点からフラットファイル(blk*.dat) | UTXOセットとブロックインデックスにLevelDB; ブロックにフラットファイル |
| UTXOストレージ | トランザクション全体をBDBに保存; すべての出力(使用済み・未使用)を保持 | 未使用出力のみをLevelDBに保存; 使用済み出力は破棄 |
| UTXO表現 | トランザクションインデックス型: 出力ごとの使用済みフラグベクトル付きフルトランザクション | アウトポイントインデックス型: 各UTXOを (txid, 出力インデックス) でキーとしコンパクトにシリアライズ |
| コインキャッシュ | 独立したキャッシュ層なし; BDBがすべての読み書きを処理 | 専用のメモリー内ライトバックキャッシュ(-dbcache、既定450 MiB) |
| ブロックストレージ | 連続的なフラットファイル(blk0001.dat、blk0002.dat等)、FAT32対応の約2 GB上限でローテーション | 連続的なフラットファイル(blk*.dat)、各約128 MiB |
| 取り消しデータ | 不要。使用済みのトランザクションもBDBのトランザクション索引に残るので、ブロックを外すときは使用済みの印を外すだけで済んだ(DisconnectInputs) | 専用のrev*.datファイルが高速なロールバックのための以前のコイン状態を保存 |
| ブロックインデックス | BDBベースのインデックス | nChainWork追跡付きLevelDBベースのインデックス |
| 剪定 | 利用不可; すべてのノードがフルチェーンを保存 | v0.11(2015年)以降利用可能; 最小保持量550 MiB |
| assumeUTXO | 利用不可 | バックグラウンド検証付きスナップショットベースのブートストラップ(v26で導入; メインネットパラメーターはv28) |
| メモリープールの永続化 | 再起動時に永続化しない | シャットダウン時にmempool.datに保存; 起動時に再読み込み |
| データベース移行 | 該当なし | v0.8(2013年)でBDB → LevelDB移行 |
| 初期ブロックダウンロード | 数分(ブロック数が少なかった) | assumeUTXOなしで数時間〜1日超; assumeUTXOスナップショット使用で数分 |
| ディスク上のサイズ | 無視できる程度(チェーンが小さかった) | アーカイブでブロックだけ約774 GB(2026年10月); 剪定で約10 GB |
BDB → LevelDB移行(v0.8、2013年3月)。サトシのオリジナル実装は、トランザクションインデックスとUTXO状態を単一のBerkeley DBデータベースに保存していた。ただし生のブロックデータはその対象になく、当初からフラットファイル(blk*.dat)に置かれていた。この選択について、サトシは2009年1月に初期ユーザーへ直接説明していた。「トランザクション対応のデータベースDBMに保存されているから、クラッシュや停電が起きても失われないはずだ」と。チェーンの成長に伴い、BDBのロック制限とメモリー特性がボトルネックとなった。v0.8リリースはUTXOセットとブロックインデックスのBDBをLevelDBに置き換えた。ブロックデータはv0.1以来使われてきたフラットファイル形式のままだった。
この移行は事件を伴った: 2013年3月11日、v0.7(BDB)を実行するノードとv0.8(LevelDB)を実行するノードが、LevelDBにはないBDBのロックカウント制限に起因してブロックの有効性について不一致となり、合意形成の分裂フォークが発生した。フォークはマイナーの協調行動により、長いv0.8チェーンを放棄する形で解決された。
9. 本ページの範囲
本ページはストレージ層を単独で扱う。以下のトピックは範囲外であり、設計文書シリーズ内のそれぞれの専門ページで扱う:
- トランザクション構造とUTXOモデル — UTXOがどう作成、使用、検証されるか。トランザクション設計ページで扱う。
- ブロック構造とチェーン選択 — ブロックがどう構成され、最も仕事量の多いチェーンがどう選択されるか。ブロックとチェーン設計ページで扱う。
- 合意規則 — ブロックがストレージ層に到達する前に受け入れるかどうかを決定する検証規則。合意形成設計ページで扱う。
- ネットワーク層のブロックリレー — ブロックがディスクに書き込まれる前にノード間でどう伝送されるか。P2Pネットワーク設計ページで扱う。
- ウォレットストレージ — 鍵導出、記述子データベース、ウォレットバックアップはここで記述した合意データとは別のストレージパスを使用する。ウォレット設計ページで扱う。










