Bitcoin Core 0.11 (ch 2): Data Storage - tezos-wiki
メインメニューを開く

tezos-wiki β

Bitcoin Core 0.11 (ch 2): Data Storage

このページでは、Bitcoin Coreがブロックチェーンデータをどのように格納するのかについて説明します。

概要編集

基本的に4つのデータが維持されています。

'blocks / blk * .dat:実際のBitcoinブロックをネットワーク形式で、ディスク上のrawファイルにダンプします。ウォレット内の欠落しているトランザクションの再スキャン、チェーンの別の部分への再編成、同期している他のノードへのブロック・データの提供にのみ必要です。

'blocks / index / *:' これは、既知のすべてのブロックに関するメタデータを含むLevelDBデータベースであり、ディスク上でそれらを見つける場所です。これがなければ、ブロックを見つけることは非常に遅いでしょう。

'chainstate / *:' これは、現在使用されていないすべてのトランザクション出力と、それらが出たトランザクションに関するいくつかのメタデータをコンパクトに表現したLevelDBデータベースです。ここにあるデータは、新しい着信ブロックとトランザクションを検証するために必要です。理論的にはブロックデータから再構築することができます(-reindexコマンドラインオプションを参照)が、これにはかなり時間がかかります。それがなければ、理論的には理論的には実際に検証を行うことができますが、使用されるすべての出力に対してブロックをフルスキャンして(2013年5月現在では7 GB)

'blocks / rev * .dat:' これらは "元に戻す"データを含んでいます。ブロックは連鎖状態の「パッチ」として表示され(未使用の出力を消費し、新しいものを生成します)、取り消しデータを逆パッチとして表示します。再編成の場合に必要な連鎖状態をロールバックするために必要です。

LevelDBは、ブロックデータから再構築できるという意味では冗長であることに注意してください。しかし、検証やその他の操作は、それがなければ耐え難いほど遅くなるでしょう。

こちらをご覧ください:StackExchange Pieter Wuille(2013)の投稿


未加工ブロックデータ(blk * .dat)編集

ブロックファイルは、ネットワークを介して受信したローブブロックを格納します。

ブロックファイルは約128 MBで、過剰な断片化を防ぐために16 MBのチャンクに割り当てられています。 2015年10月現在、ブロックチェーンは約365個のブロックファイルに格納され、合計約45 GBになります。

各ブロックファイル(blk1234.dat)には、対応する元に戻すファイル(rev1234.dat)があります。このファイルには、再編成(fork)の際にブロックチェーンからブロックを削除するために必要なデータが含まれています。

ブロックファイルに関する情報は、ブロックインデックス(LevelDB)に2か所に格納されます。

  • ファイル自体に関する一般的な情報は、ブロック索引LevelDB(「fxxxx」キー、「xxxx」は4桁のファイル番号)の「f」レコードに保持されます。
    • ファイルに格納されているブロック数
    • ファイルサイズ(および対応する元に戻すファイルサイズ)
    • ファイル内の最下位ブロックと最上位ブロック
    • タイムスタンプ - ファイル内の古いブロックと最新のブロック
  • ディスク上の特定のブロックがどこにあるかについての情報は、 "b"( "b" =ブロック)レコードにあります:
    • 各ブロックには、ディスク上のブロックへのポインタが含まれています(ファイル番号とオフセット)


'コードからブロックデータファイルにアクセスする'

ブロックファイルには、次の方法でアクセスします。

1)DiskBlockPos:ディスク上のブロックの位置を指すポインタ(ファイル番号とオフセット)。

2) vInfoBlockFiles :BlockFileInfoオブジェクトのベクトル。この変数は、次のようなタスクを実行するために使用されます。

  • 新しいブロックが現在のファイルに収まるか、新しいファイルを作成する必要があるかを決定する
  • ブロック&アンドゥファイルでディスクの総使用量を計算する
  • ブロックファイルを繰り返して、整理できるものを探します

ブロックは、AcceptBlockで受信されるとすぐにディスクに書き込まれます。 (実際のディスク書き込み操作はWriteBlockToDisk [ main.cpp:1164 ]にあります)。ブロックファイルにアクセスするコードと、コインデータベース(/ chainstate)にアクセスして書き込むコードとは、一部重なり合っていることに注意してください。状態をディスクにフラッシュする複雑なシステムがあります。このコードはブロックファイルには影響しません。ブロックファイルは受信時にディスクに書き込まれます。それらが受信され、格納されると、ブロックファイルは、他のノードへのブロックを提供するためにのみ必要とされる。


'ブロックファイルに関する詳細'

こちらをご覧ください:[1]

ブロックインデックス(leveldb)編集

ブロック索引は、ブロックがディスクに格納されている場所を含む、すべての既知のブロックに関するメタデータを保持します。

「既知のブロック」のセットは、受信し処理したがアクティブチェーンの一部ではないブロックを含むため、最も長いチェーンのスーパーセットであることに注意してください。たとえば、アクティブチェーンから切り離された孤立したブロック再編。

用語

なぜなら、人々は通常、「ブロックチェーン」がアクティブチェーン(起点ブロックから始まり、現在のヒントに続くXブロックの中断されていない線形連鎖)と同義であると考えているからです。コード内のいくつかの場所では、「ブロックチェイン」はアクティブチェーンと、ノードが知ることになるチェーンから数多くの短いフォークを指します。

a)ブロックツリー

ディスク上に保存されている既知のブロックセットのより良い用語は、「ブロックツリー」です。この用語は、主チェーンからの多数の分岐(小さなものですが)を持つツリー構造を想定しています。実際、ブロックインデックスLevelDBは、 src / txdb.h で定義された "CBlockTreeDB"ラッパークラスを介してアクセスされます。実際には、異なるノードにはわずかに異なるブロックツリーがあることは間違いありません。重要なことは、彼らがアクティブチェーンに同意していることです。

'Key-Valueのペア'

実際のLevelDBの内部では、使用されるキーと値のペアは次のとおりです。

    'b' + 32バイトのブロックハッシュ - >ブロックインデックスレコード。各レコードには、

  • ブロックヘッダ
  • 高さ。
  • トランザクション数。
  • このブロックの有効性はどの程度まで検証されていますか?
  • ファイル内で、そのファイルのどこにブロックデータが格納されているか。
  • ファイル内で、そのファイルのどこにUNDOデータが保存されているか。

   'f' + 4バイトのファイル番号 - >ファイル情報レコード。各レコードには、

  • その番号のブロックファイルに格納されているブロックの数。
  • その番号のブロックファイルのサイズ($ DATADIR / blocks / blkNNNNNN.dat)。
  • その番号のブロックファイルに格納されているブロックの最低高さと最高高さ。
  • その番号のブロックファイルに格納されているブロックの最低と最高のタイムスタンプ。

    'l' - > 4バイトのファイル番号:使用された最後のブロックファイル番号。

    'R' - > 1バイトのブール値(trueの場合は '1'):再インデックス処理中かどうか。

    'F' + 1バイトのフラグ名の長さ+フラグ名の文字列 - > 1バイトブール値(真の場合は '1、偽の場合は' 0 '):オンまたはオフにできるさまざまなフラグ。現在定義されているフラグには、

  • 'txindex':トランザクション索引が使用可能かどうか。

    't' + 32バイトのトランザクションハッシュ - >トランザクションインデックスレコード。これらはオプションで、 'txindex'が有効な場合にのみ存在します(上記参照)。各レコードには、

  • トランザクションが格納されているブロックファイル番号。
  • そのファイルに、トランザクションが含まれているブロックのオフセットが格納されます。
  • ブロックの先頭からそのトランザクション自体が格納されている位置までのオフセット。


参照してください:StackExchange postピーテル・ウイル(2014年)


'データアクセス層'

データベースは、CBlockTreeDBラッパークラスを介してアクセスされます。 txdb.h を参照してください。

ラッパーは、 main.cppに定義されているpblocktreeというグローバル変数でインスタンス化されます。

CBlockIndex

データベースに格納されたブロックは、CBlockIndexオブジェクトとしてメモリ内に表されます。このタイプのオブジェクトは、 ヘッダ が受信された後に最初に作成されます。完全なブロックの受信を待つことはありません。ヘッダーがネットワーク経由で受信されると、ヘッダーはCBlockHeadersのベクトルにストリームされ、次にチェックされます。チェックアウトする各ヘッダーは、新しいCBlockIndexを作成し、データベースに格納します。

CBlock / CBlockHeader

これらのオブジェクトは/ blocks LevelDBとほとんど関係がないことに注意してください。 CBlockは、ブロック内のトランザクションの完全なセットを保持します。そのデータは、full形式、raw形式、blk ???。datファイル、UTXOデータベースのプルーン形式の2つの場所に格納されます。ブロック索引データベースは、ブロックのメタデータのみを保持するため、そのような詳細は考慮されません。   ブロックデータベースをメモリにロードする

起動時にデータベース全体がメモリにロードされます。 LoadBlockIndexGuts( txdb.cpp )を参照してください。これには数秒しかかかりません。

ブロック( 'b'キー)はグローバルな "mapBlockIndex"変数にロードされます。 "mapBlockIndex"はブロックツリー全体のブロックごとにCBlockIndexを保持するunordered_mapです。アクティブチェーンだけではありません。

mapBlockIndexについては、第6章「ブロックチェーン」で詳しく説明しています。

ブロックファイルメタデータ( 'f'キー)は、vInfoBlockFilesにロードされます。

UTXOセット(chainstate leveldb)編集

UTXOデータベースは、2012年に- "Ultraprune"に導入されました。

「Ultraprune」の背後にあるアイデアは、過去のトランザクションのサイズを縮小し、後のトランザクションを検証するために必要な過去のトランザクションの部分だけを保持することです。

2つの入力を取り、O1、O2、O3の3つの出力に送るトランザクションT1があるとします。これらの出力のうちの2つ(O1、O2)は、後のトランザクションT2の入力として使用されています。 T2がマイニングされると、T1は1つの関心項目(O3)しか持たない。 T1を完全に守る理由はありません。代わりに、O3(スクリプトと量のロック)とT1に関する特定の基本情報(高さ、コインベースであるかどうかなど)だけで構成されたT1のスリム化バージョンで十分です。

ultrapruneの説明はpull内の特定の "ultraprune"コミットにあります:

:-------------

:ビットコインのトランザクション/ブロック検証ロジックを切り替えて、未使用のトランザクション出力スクリプト、金額、高さをすべて含む「コインデータベース」を使用します。

:ultrapruneという名前は、完全なトランザクションインデックスの代わりに、未使用の出力を持つインデックスのみを保持する必要があるという事実から来ています。今のところ、ブロック自体は、通常どおりに保たれますが、サービング、再スキャン、再編成にのみ必要です。

:基本的なデータ構造はCCoins(単一トランザクションのコインを表す)、CCoinsView(コインデータベースの状態を表す)です。 CCoinsViewにはいくつかの実装があります。コインデータベース(coins.dat)に裏打ちされたもの、メモリプールに裏打ちされたもの、そしてその上にキャッシュを追加するもの。 FetchInputs、ConnectInputs、ConnectBlock、DisconnectBlock、...は、汎用のCCoinsViewで動作するようになりました。

:ブロックスイッチングロジックは、変更が加えられる前にデータベースにコミットされる変更を含む単一のキャッシュCCoinsViewを構築するようになりました。つまり、コミットされていない変更はデータベースから読み取られることはなく、LevelDBのようなトランザクションをサポートしていない(アトミック書き込みをサポートする)別のデータベース層への移行を容易にするはずです。

:getrawtransaction()RPC呼び出しでは、txid-to-disk索引へのアクセスが適しています。このインデックスは実装の他の部分では必要ないし、有用ではないため、提供されていません。代わりに、getrawtransaction()はコイン・データベースを使用してブロックの高さを検出し、そのブロックをスキャンして要求されたトランザクションを検出します。これは遅いですが、デバッグには十分です。

:-----------------


参照:Ultraprune - July 2012 用語

"UTXO(Unspent Transaction Out):"トランザクションからの出力。これは通称「コイン」と呼ばれています。このため、UTXOデータベースは「コインデータベース」と呼ばれることがあります。

"UTXO set / coins database / chainstate database:"これらの用語は多かれ少なかれ同義語であり、互換的に使用されています。

"Provably Unspendable:"コインは、そのscriptPubKeyを満たすことができない場合、例えば、OP_RETURNのように、代償不可能です。明らかに差し替え不可能なコインは、額にかかわらずutxoデータベースから削除することができます。


'Key-Valueのペア'

chainstate levelDBのレコードは次のとおりです。

    そのトランザクションの 'c' + 32バイトのトランザクションハッシュ - >未使用トランザクション出力レコード。これらのレコードは、最低1つの未使用出力が残っているトランザクションに対してのみ存在します。各レコードには

  • トランザクションのバージョン。
  • 取引がコインベースであったかどうか。
  • どの高さブロックにトランザクションが含まれているか。
  • そのトランザクションの出力は未使用です。
  • 未使用出力のscriptPubKeyと金額。

    'B' - > 32バイトのブロックハッシュ:データベースが未使用トランザクションの出力を表すブロックハッシュ。

参照してください:StackExchange postピーテル・ウイル(2014年)

'データアクセスレイヤとキャッシュ'

UTXOデータベースへのアクセスはブロック索引よりかなり複雑です。これは、その性能がBitcoinシステムの全体的なパフォーマンスにとって非常に重要なためです。数十万ブロックしかないので、ブロック索引はパフォーマンスにとってあまり重要ではありません。まともなハードウェア上で実行されているノードは、数秒でそれらを検索してスクロールできます(非常に頻繁に行う必要はありません)。 UTXOデータベースには何百万もの硬貨があり、mempoolに入ったりブロックに入っている各トランザクションの入力ごとにチェックして変更する必要があります。

sipaがultrapruneコミットで言ったように: :基本的なデータ構造はCCoins(単一トランザクションのコインを表す)、CCoinsView(コインデータベースの状態を表す)です。 CCoinsViewにはいくつかの実装があります。コインデータベース(coins.dat)に裏打ちされたもの、メモリプールに裏打ちされたもの、そしてその上にキャッシュを追加するもの。

しかしこれは明白ではないかもしれませんが、少なくとも、コードの現在の状態ではありません。

0.11では、CoinsViewのインスタンス化は次のとおりです。

  • ダミー
  • データベース
  • pCoinsTip(データベースにバックアップされたキャッシュ)
  • "検証キャッシュ"(pCoinsTipによってサポートされ、ブロック接続時に使用されます)

キャッシュのチェーンとは別に、データベースによってバックアップされるメモリプールのCoinsViewがあります。


ビューのクラス図(データ型)は次のとおりです。

      CCoinsView (抽象クラス)
             /            \
         ViewDB          ViewBacked 
      (データベース)          /   \
                   ViewMempool   ViewCache


各クラスには1つの重要な特性があります。

  • ビューは基本クラスで、コインが存在することを確認するメソッド(HaveCoins)、コインを取得するメソッド(GetCoins)などを宣言するメソッドです。
  • ViewDBにはLevelDBと対話するコードがあります。
  • ViewBackedに別のViewへのポインタがあります。 したがって、UTXOセットの別のビュー(バージョン)によって「バックアップ」されます。
  • ViewCacheにはキャッシュ(CCoinsのマップ)があります。
  • ViewMempoolは、mempoolをビューに関連付けます


それらは定義されたクラスです。 オブジェクト図は次のとおりです


           データベース       
           /       \
       MemPool     ブロックチェーンキャッシュ (pcoinsTip) 
     View/Cache            \
                         検証キャッシュ


ビューのインスタンス化を要約した表は次のとおりです。

オブジェクト タイプ 裏づけ? 説明 / 目的
DB view ViewDB 該当なし チェインステートLevelDBに従ってUTXOを表します。 / コインを回収し、LevelDBへの変更をフラッシュします。
コードの作成(インスタンス化):参照 init.cpp:1131
pCoinsTip
(ブロックチェーンキャッシュ)
ViewCache DB view アクティブチェーンの先端に対応するUTXOセットを保持します。 取得したデータベースビューにフラッシュします。
コードの作成:参照init.cpp:1133
検証キャッシュ ViewCache pCoinsTip このキャッシュの有効期間は、ConnectTip(またはDisconnectTip)内にあります。
その目的は、ブロックの処理中にUTXOセットの変更を追跡することです。
ブロックが検証されると、キャッシュはpcoinsTipにフラッシュされます。
ブロックが失敗すると、キャッシュは破棄されます。
コードの作成:参照 main.cpp:2231: コインビューキャッシュビュー(pcoinsTip);
Mempool view ViewMemPool pCoinsTip このオブジェクトはmempoolをビューに持ち込みます。つまり、UTXOセットとmempoolの両方を見ることができます。
その目的は、「ゼロ確認」トランザクションであるトランザクションの連鎖の検証を可能にすることである。 (トランザクションの連鎖が許されない場合、mempoolはpcoinsTipに対して単に検証することができます)。
したがって、照会されると、与えられた入力がmempool(すなわち、 "zero-conf")またはブロックチェーンのutxo set( "confirmed")のいずれかに見つかるかどうかをチェックできます。
このオブジェクトはキャッシュではありません。 むしろ、以下のオブジェクトによって使用されるビューであり、キャッシュを含んでいます。
コードの作成:その寿命はAcceptToMemoryPoolの寿命です main.cpp.
Mempool cache ViewCache Mempool view mempoolのキャッシュ。 キャッシュを含み、そのバックエンドをmempoolビューに設定します。
コードの作成:その寿命はAcceptToMemoryPoolの寿命ですmain.cpp.


UTXOセットの読み込み

コインデータベースへのアクセスは init.cpp で初期化されます:1131-1133:

 pcoinsdbview =新しいCCoinsViewDB(nCoinDBCache、false、fReindex);  pcoinscatcher =新しいCCoinsViewErrorCatcher(pcoinsdbview);  pcoinsTip =新しいCCoinsViewCache(pcoinscatcher);

コードは、CoinsViewDBを初期化することから始まります。このCoinsViewDBには、LevelDBからコインをロードするメソッドが装備されています。
エラーキャッチャーは無視できる小さなハックです。
次に、コードはアクティブチェーンの状態を表すキャッシュであるpCoinsTipを初期化し、データベースビューによってバックアップされます。


キャッシュとデータベース

coins.cppのFetchCoins関数は、コードがデータベースとキャッシュをどのように使用するかを示しています。  1 CCoinsMap :: iterator it = cacheCoins.find(txid);  2 if(it!= cacheCoins.end())  3それを返す。  4 CCoins tmp;  5 if(!base-> GetCoins(txid、tmp))  6 return cacheCoins.end();  7 CCoinsMap :: iterator ret = cacheCoins.insert(std :: make_pair(txid、CCoinsCacheEntry()))最初に;

まず、コードは、キャッシュ内で特定のトランザクションIDのコインを検索します。 (1行目)
見つかった場合は、「フェッチされた」コインを返します。 (行2-3)
そうでなければ、データベースを検索します。 (5行目)
データベースに見つかった場合は、キャッシュを更新します。 (7行目)

注:キャッシュのバックエンドが別のキャッシュである場合、「データベース」という用語は実際には「親キャッシュ」を意味します。

検証キャッシュをブロックチェーンキャッシュにフラッシュする

検証キャッシュは、ブロックを接続した後、範囲外になる直前にブロックチェーンキャッシュにフラッシュされます。スコープはConnectTipでキャプチャされます。具体的には、コードブロック main.cpp :2231-2243にあります。そのコードブロックには、ConnectBlockの呼び出しがあり、その間にコードは新しいコインを検証キャッシュに格納します。 (具体的には、 main.cpp のUpdateCoins()を参照してください)。コードブロックの最後に、検証キャッシュがフラッシュされます。その「親ビュー」もキャッシュ(pcoinsTip、「ブロックチェーンキャッシュ」)なので、コードは親のViewCache :: BatchWriteを呼び出し、更新されたコインエントリをそれ自身のキャッシュに入れ替えます。 (動作中の多態性:後で、ブロックチェーンキャッシュがデータベースビューにフラッシュすると、コードはCoinsViewDB :: BatchWriteを実行します。最後の行はLevelDBに書き込まれます)。

要約すると、検証キャッシュの使用は簡単です。前述のコードブロックで、インスタンス化され、使用され、フラッシュされ、範囲外になります。


ブロックチェーンキャッシュをデータベースにフラッシュする

バリデーションキャッシュをフラッシュすることは簡単でした。なぜなら、コードはメモリ内の2つのキャッシュ間でアイテムをシャッフルしただけでした(そのうちのどれもがキャッシングコードの外には気づいていませんでした)。最も低いレベルでは、ブロックチェーンキャッシュ(pcoinsTip)をフラッシュする仕組みは、検証キャッシュと同じです。Flush()メソッドはバックエンド(「ベース」ポインタ)でBatchWriteを呼び出します。データベースビュー。レベルを上げると、FlushStateToDisk(FSTD) - main.cpp :2098からFlush()が呼び出されます。 FlushStateToDiskは、いくつかの異なる点で、指定された 'モード'で呼び出されます:


フラッシュモード 説明 呼び出されたとき
必要に応じて キャッシュがサイズ制限を超えている場合に限りフラッシュします。 ブロックを接続(または切断)し、検証キャッシュをフラッシュした直後。
参照 ConnectTip / DisconnectTip.
常に キャッシュをフラッシュします。 初期化中のみ。
定期的 ここで、コードは、他のデータポイントがフラッシュするかどうかを決定することを考慮する。
コードはサイズの限界をはるかに超えていますか?
キャッシュがフラッシュされてからずっと時間がかかりましたか?
そうなら、次に進んでください。
(コードのコメント: "定期的に変更をディスクに書き込んだ後、リレー")。


このアイデアは、ブロックキャッシュを頻繁に(プログラムがクラッシュした場合に多数のブロックをダウンロードしなくても済むように)フラッシュすることですが、コインキャッシュを頻繁にキャッシュしないようにしています(コインキャッシュのメリットを最大限に引き出すためです)。

具体的には、ブロックキャッシュは1時間に1回フラッシュされることが保証され、コインは1日に1回キャッシュされる。 (See:PR 6102のSipa comment

FlushStateToDiskコードはよくコメントされているので、もっと興味深い読者は main.cpp をチェックすることができます。

未処理の元に戻すデータ(rev * .dat)編集

取り消しデータには、ブロックを切断または「ロールバック」するために必要な情報、具体的には問題のブロックによって費やされたコインが含まれます。

したがって、書き込まれるデータは基本的にCTxOutオブジェクトのセットです。 (CTxOutは単に量とスクリプトです - primitives / transaction.h :107-108を参照してください)。

この問題は、コインがトランザクションによって費やされた最後のコインである場合、トランザクションのメタデータ(コインベースであるかどうか、txnのブロックの高さ)を格納する必要があるという事実によって、少し複雑になります。 O1、O2、O3の順番でO1、O2、O3が出力されたトランザクションTがあります。取り消しファイルに書き込まれるのはすべて金額とスクリプトです。 03の場合、元に戻すファイルには、量、スクリプト、Tの高さとバージョン、およびTがコインベースであるかどうかが含まれます。

元に戻すデータは、次のコードを使用して生ファイルに書き込まれます。  fileout << blockundo; (main.cpp:1567 [UndoWriteToDisk])

このコード行はCBlockUndoのシリアライゼーション機能を呼び出します。これは基本的にコインのベクトル(CTxOuts)です。最後に、チェックサムがアンドゥファイルに書き込まれます。チェックサムは初期化中に使用され、チェックされている元に戻すデータが完全であることを確認します。 Pull 2145を参照してください。

アンドゥデータは、ブロックを切断するときに使用されます。 DisconnectBlock()コードについては、このWikiページのThe Blockchain:Reorganizationsで詳しく説明しています。

LevelDBの使用編集

LevelDBは、2012年に複雑な "Ultraprune"プル(PR 1677)の一部としてブロックインデックスとUTXOセット(チェーンステート)を格納するために導入されたキーバリューストアです。こちらをご覧ください:[https://github.com/bitcoin/bitcoin/pull/1677/ Ultrapruneの27件のコミットをコミットしてください。

なぜLevelDBが使われているのかについては、コア開発者のGreg Maxwellが次のことをbitcoin-dev mailing 2015年10月のリスト

人々は "それは<データベース>だと思っていますが、私はそれを<ブラックボックス>と考えています。アプリケーションとニーズはここで非常に特化しています。 。 。非常にビットコイン固有の暗号コンセンサスアルゴリズムの裏には、既存の高性能のキー値ストアが収まるスロットがありました。それで私たちは1つを使用していて、いくらか努力しています...

異なるノードが異なるデータベースを使用できるかどうかを尋ねるかもしれません - 同じデータを検索する限り、違いは何ですか?ここでの問題は「バグのためのバグの互換性」です。特定の状況下でレコードが返されないようなバグがあるデータベースの場合、他のすべてのノードに同じバグがあります。

Greg Maxwellは、上記で参照されているスレッドと同じスレッド(http://bitcoin-development.narkive.com/XAPoxKZU/patch-switching-bitcoin-core-to-sqlite-db)を次のように述べました(sqlite )]:

... [D]データベースにエラーが発生し、レコードが返されないか、古いデータが返されないことがあります。そして、それらの存在が整合性を維持しなければならない場合。バグを「修正」することで、コンセンサス状態で発散する可能性があり、ユーザーが盗難に遭う可能性があります。
Bitcoin Coreでのleveldbの使用に先立ち、稀な状況下で実際にそこにあったレコードに一貫して戻らないバグがありました。 。 。 Leveldbはマイナーアップデートでこの重大なバグを修正しました。しかし、このような修正をビットコインネットワークに無制限に導入すると、コンセンサス状態になる可能性があります。そのような修正プログラムは整然とした方法で展開する必要があります。

関連項目編集

MediaWiki spam blocked by CleanTalk.