Contact

Bitcoin Core v28.0からサポートされたスナップショットベースの同期

  • CTO Tech blog

Bitcoin Core v28.0以降からサポートされたAssumeUTXOを用いた新たな同期モードを試してみた。

ブロックチェーンの同期

ブロックチェーンの同期とは、チェーンの状態の同期を意味する。Bitcoinの場合、状態とはUTXOセットの状態を指す。ノードを初めて起動する(もしくはしばらく起動していなかった)場合、状態を同期するためにIBD(Initial Block Download)が実行される。これはジェネシスブロックから最新のブロックまでのすべてのトランザクションを実行し、その結果として現在有効なUTXOセット(状態)が決定される。

チェーンの成長と共に、IBDにかかる時間も増加していったため、これまでもさまざまな改善が行われてきた↓

https://techmedia-think.hatenablog.com/entry/2017/03/09/122257#IBDのパフォーマンス改善

ただ、すべてのトランザクションの処理において、UTXOセットの挿入/削除を行いUTXOセットを完成させるという処理自体は変わっていなかった。

AssumeUTXOとは?

AssumeUTXOは、Bitcoin Coreにハードコードされたチェーンのあるブロック高において有効なUTXOセットへのコミットメント。mainnetではブロック高840,000のUTXOセットのパラメーターが設定されている↓

https://github.com/bitcoin/bitcoin/blob/v28.0/src/kernel/chainparams.cpp#L186-L193

あるブロック高におけるAssumeUTXOとそのブロック高のスナップショットを使用してIBDを高速化するのが28.0から導入された新機能。あるブロック高時点のUTXOセットのスナップショットを作成し、IBD中のノードでそのUTXOセットをロードできるようにすることで、そのブロック高より先のブロックのトランザクションを処理することで、より早くチェーンの先端に同期できるようにしようというアプローチ。直接比較はし辛いけど、EthereumでいうSnap syncのようなアプローチ*1

ただ、スナップショットを用いて同期しても、バックグラウンドで、ジェネシスブロックからスナップショットのブロック高までのブロックのダウンロードおよび検証が行われるので、IBDの時間は速くなるけど、使用される帯域幅が減少することはない。ちゃんと最初から検証するところがBitcoinらしい。

※ 以前のバージョンのBitcoin Coreにもスナップショットをダンプ/ロードするRPC(dumptxoutset/loadtxoutset)は既に提供されていたけど、AssumeUTXOがハードコードされていなかったので、スナップショットのロードはできなかった。

スナップショットの入手

現状、スナップショットをP2Pネットワークを介してやりとりする機能は無いので、別の手段で入手する必要がある。ブロック高840,000のスナップショットのデータは、以下のBitTorrentのマグネットリンクからダンロードできる(約9.8GB)。

magnet:?xt=urn:btih:596c26cc709e213fdfec997183ff67067241440c&dn=utxo-840000.dat&tr=udp%3A%2F%2Ftracker.bitcoin.sprovoost.nl%3A6969

他に、dumptxoutset RPCを使ってスナップショットをダンプする方法も考えられるけど、dumptxoutsetはそのノードが認識しているチェーンの先端におけるUTXOセットに対してスナップショットを生成するRPCで、ブロック高を指定して実行することができない。Bitcoin Coreでは、ブロック高を指定してスナップショットを作成するシェルが一応提供されてる↓

https://github.com/bitcoin/bitcoin/blob/v28.0/contrib/devtools/utxo_snapshot.sh

ただ、Bitcoin Coreには特定のブロック高における状態を取得できるアーカイブノードのような機能はないので、↑では、対象のブロック高の1つ先のブロックをinvalidateblock RPCで無効にすることで、対象ブロックまで状態をロールバックさせ、そこでdumptxoutsetを実行してる。

スナップショットのロード

スナップショットのファイルが入手できたら、loadtxoutset RPCを実行する。

$ bitcoin-cli -rpcclienttimeout=0 loadtxoutset <スナップショットファイルのパス>

注意事項として、

  • 予めブロックヘッダーの同期は終わっている必要がある。
  • -rpcclienttimeout=0オプションを指定して実行しないと、途中でRPCのタイムアウトになる。

進行状況は、debug.logに出力されるログで確認できる↓

2025-01-27T02:32:00Z [snapshot] 156000000 coins loaded (88.16%, 210 MB)

約2時間でスナップショットのロードは終わった。

スナップショットのブロック高は840,000なので、ここから現在の最新のブロックまでの同期が始まる。この時の最先端のブロック高は881,011で、41,011ブロックの同期が行われる。これに2時間ちょっとかかった。

トータルで4時間ちょっとでmainnetのチェーンの同期が完了した。これまで2,3日かかってたのを考えるとだいぶ早く同期ができる*2

その後もバックグラウンドでジェネシスブロックからスナップショットのベストブロックまでのブロックのダウンロードは進み、進行状況はgetchainstates RPCで確認できる↓

$ bitcoin-cli getchainstates
{
  "headers": 881012,
  "chainstates": [
    {
      "blocks": 416570,
      "bestblockhash": "0000000000000000023c1c4d7e72f0fb53e34fa6f3c409d6821bd51ba951dff8",
      "difficulty": 196061423939.65,
      "verificationprogress": 0.1179632528891298,
      "coins_db_cache_bytes": 7969177,
      "coins_tip_cache_bytes": 382520524,
      "validated": true
    },
    {
      "blocks": 881012,
      "bestblockhash": "000000000000000000007a410005ae80fddf0ec0cdb1eae1cb544d71d2da036f",
      "difficulty": 108105433845147.2,
      "verificationprogress": 0.9999977461662677,
      "coins_db_cache_bytes": 419430,
      "coins_tip_cache_bytes": 20132659,
      "snapshot_blockhash": "0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5",
      "validated": false
    }
  ]
}

実際にBitcoin Coreのデータディレクトリには

  • chainstate
  • chainstate_snapshot

という2つのディレクトリが作成されている。Assume UTXOのドキュメントによると、バックグランドの同期が完了後、Bitcoin Coreを再起動すると、バックグランドで同期したchainstateがクリーンアップされ、chainstate_snapshotディレクトリがchainstateにリネームされるみたい。そして、その後のchainstateディレクトリ内にbase_blockhashファイルが残っていたら、スナップショットから同期されたことを示すようになる。

他のノード機能の影響

スナップショットを利用すると従来の同期方法と比べてかなり早く同期ができるけど、この方法で同期した場合、ノードの他の機能に以下の影響がある。

インデックス

txindex=1でインデックスを有効にしている場合、インデックス自体は機能するけど、インデックスの生成はジェネシスブロックから順に行われるため、実際に作成されるインデックスはバックグラウンドで行われる検証に依存する。スナップショットをロードしてもその分のインデックスは作成されない。インデックスの同期状況は、getindexinfo RPCで確認できる↓

$ bitcoin-cli getindexinfo  
{
  "txindex": {
    "synced": true,
    "best_block_height": 413126
  }
}

スナップショットの同期は完了してても、↑のようにインデックスはまだ追いついていない。

プルーニング

プルーニングしているノードもスナップショットをロードできる。ただ、-pruneの設定は最小値が550MBになっているけど、この設定をしていたとしても少なくとも1100MB使用するようになる。

バックグラウンドの同期が実行中の間は、一時的にchainstateディレクトリが2つある状態になり、その分ディスク使用量が増加する。

*1:同期問題はEthereumの方が深刻なので、同期方法もこれまでいろんなアプローチが採られている。

*2:後半のブロックの同期中にoom-killerによってログインセッションが破棄されたのがちょっと気になる。再ログインしてcoreを起動するとそのまま同期が続いたけど

ブログ本文へのリンク

ブログ本文へはこちらから

https://techmedia-think.hatenablog.com/entry/2025/01/27/152016

Chaintopeでブロックチェーンの未来を共に創りませんか?

Chaintopeは、独自のブロックチェーン「Tapyrus」と、開発プラットフォーム「Tapyrus Platform」を活用し、デジタル社会の信頼基盤を構築しています。
私たちは、ブロックチェーン技術の可能性を最大限に引き出し、社会に新しい価値を提供することを目指しています。

募集職種:

ブロックチェーンエンジニア
アプリケーションエンジニア
インフラ・保守エンジニア
プロジェクトマネージャー
フィールドセールス

Chaintopeで働く魅力:

最先端のブロックチェーン技術に触れる機会
リモートワークやフレックスタイム制による柔軟な働き方
専門性の高いチームとの協働
ブロックチェーン技術に情熱を持つあなたのスキルを、私たちのチームで活かしませんか?
詳細は、採用情報をご覧ください。

https://www.chaintope.com/recruit/

 

話題のキーワード