XRP Ledger・コンセンサス・プロセス

著者: Dave Cohen, David Schwartz and Arthur Britto.

この記事では、Rippleレジャーの高レベルな概要、それが格納する情報、およびトランザクションがどのようにレジャーを変更するかを説明します。

Rippleでアプリケーションを構築する場合は、Ripple APIの動作とレジャーへの影響に驚かないように、このプロセスを理解することが重要です。

重要: トランザクションはRippleレジャーに瞬時に適用されません。トランザクションの影響が適用されるまでには時間がかかります。このプロセスの間、Ripple APIは最終的で不変的な結果であると誤解されるべきではない暫定的な結果を返すことがあります。不変的な結果は、検証されたレジャーを調べることによってのみ決定できます。

イントロダクション

Rippleネットワークは、アプリケーションに口座残高とオファー(売買申し込み)の状態に関する信頼できる情報を提供する世界的規模の共有レジャーを提供します。この状態情報は次のものを含みます:

  • 各アカウントの設定
  • アカウント間の残高
  • 通貨を売買するオファー
  • 手数料や準備金のようなネットワーク設定
  • タイムスタンプ

図1: Rippleレジャーの要素

 

Rippleネットワークは、数秒ごとに新しいレジャー・インスタンスを生成します。それに先行するレジャーは、レジャーの履歴を構成します。最新の検証されたレジャーであっても、少し前の時点のネットワークの状態を表す履歴の一部です。現時点で、ネットワークは次のレジャー・インスタンスで適用されファイナライズされる可能性のあるトランザクションを評価しています。

図2: レジャー・シーケンスのタイムライン

 

レジャー・インスタンスはそのシーケンス番号によって一意に識別されます[1]。レジャーはインクリメンタルに番号が付けられます。最後に検証されたレジャーが N の場合、その前は N-1 で、その次は N+1 になります。レジャーN+1は、レジャーNにトランザクションのセットを適用することによって生成されます。

レジャーへのユーザーレベルの変更は、トランザクションの結果です。トランザクションの例としては、支払い、アカウントの設定やトラストラインの変更、取引のオファーなどがあります。各トランザクションはレジャーへの変更を承認し、アカウントの所有者によって暗号的に署名されます。トランザクションは、アカウントの変更を承認する唯一の方法です。

レジャー・インスタンスには、それらのトランザクションに関するトランザクションとメタデータのセットも含まれています。それらのトランザクションは、新しいインスタンスを作成するために、前のレジャーに適用されたものです。メタデータは、トランザクションのレジャーへの影響を正確に記録します。

図3: レジャー・シーケンスに適用されたトランザクション

 

レジャー・インスタンスに含まれるトランザクションのセットは、そのレジャーに記録され、Rippleレジャー履歴の監査能力を可能にします。レジャーN+1のアカウント残高がレジャーNのアカウント残高と異なる場合、レジャーN+1にはその変更の原因となったトランザクションが含まれます。

検証されたレジャー内に表示されるトランザクションは、レジャーの変更に成功したか、または要求されたアクションを実行せずに処理された可能性があります。成功したトランザクションには、要求された変更がレジャーに適用され、手数料が請求されたことを示す tesSUCCESS 結果コードを持ちます。レジャー内の他のトランザクションには、手数料を請求するだけで、他の変更を実行しないトランザクションであることを示す tecクラスの結果コードを持ちます[2]。

tecクラスのトランザクションは、手数料を請求する際にアカウント残高を変更するため、レジャーに含まれます。

tes と tec クラスの結果コードに加えて、ter、tef および tem クラスのコードがあります。後者の3つは、APIの呼び出しによって返された暫定的な失敗を示します。これら3つのエラーのクラスは、検証されたレジャーには表示されません。

Ripple APIを使用する場合、アプリケーションは、レジャーに含めるように提案された候補トランザクションと、検証されたレジャーに含まれる検証済みのトランザクションを区別する必要があります。検証されたレジャー内にあるトランザクションの結果(result)のみが不変です。候補トランザクションは、検証されたレジャーに含まれることはありません。

重要: いくつかのRipple APIは、候補トランザクションに基づいて暫定的な結果を提供します[3]。アプリケーションは、トランザクションの最終的な結果を決定するために暫定的な結果に決して頼るべきではありません。最終的にトランザクションが成功したことを確実に知る唯一の方法は、そのトランザクションが検証されたレジャー内にあり、尚且つ結果コード tesSUCCESS を持つまでトランザクションの状態をチェックすることです。もし、トランザクションが他の結果コードを持ち、有効なレジャー内にある場合、そのトランザクションは失敗しています。もし、トランザクションの LastLedgerSequence 内で指定されたレジャーが検証済みであるにもかかわらず、そのトランザクションがそのレジャーまたはそれ以前のその他のレジャー内に表示されない場合、そのトランザクションは失敗しており、その他のレジャーに表示されることはありません。検証されたレジャー内に表示されるトランザクションの場合にのみ結果は最終的であるか、または、このドキュメントで後述するように、LastLedgerSequence の制限により表示されることがありません。

Rippleプロトコル – コンセンサスとバリデーション(検証)

Rippleネットワークは、トランザクションを承認、処理するノードと呼ばれる分散サーバーによって構成されています。クライアントアプリケーションは、トランザクションに署名してノードにトランザクションを提出します。ノードは、これらの候補トランザクションを処理するためにネットワークを通して中継します。クライアントアプリケーションの例には、モバイルおよびウェブウォレット、金融機関へのゲートウェイ、電子取引プラットフォームなどがあります。

図4: Rippleプロトコルの参加者

 

トランザクションを受信、中継、処理するノードは、トラッキングノードまたは検証ノードのいずれかです。トラッキングノードの主な機能には、クライアントからのトランザクションの配布、およびレジャーに関するクエリへの応答が含まれます。検証ノードは、トラッキングノードと同じ機能を実行し、さらにレジャーシーケンスの進展に寄与します[4]。

クライアントアプリケーションによって送信されたトランザクションを受け入れる間、各トラッキングノードは最後に検証されたレジャーを開始点として使用します。受け入れられたトランザクションは候補です。ノードは候補トランザクションをピアに中継し、候補トランザクションをネットワーク全体に伝播させます。理想的には、それぞれのノードが最後に検証されたレジャーに適用するために同一のトランザクションのセットを考慮するために、各候補トランザクションは全てのノードに知られているべきです。しかし、トランザクションが伝搬するのに時間がかかるため、ノードは、常に同じ候補トランザクションのセットで動作するわけではありません。これを考慮に入れて、Rippleネットワークは、コンセンサスと呼ばれるプロセスを使用して、同一のトランザクションが処理され、検証されたレジャーがネットワーク全体で一貫していることを保証します。

コンセンサス

ネットワーク上のノードは、候補トランザクションに関する情報を共有します。コンセンサス・プロセスを通じて、検証ノードは、候補トランザクションの特定のサブセットが、次のレジャーで考慮されることに同意します。コンセンサスは、ノードが提案または候補トランザクションのセットを中継する反復プロセスです。ノードは、ピアの大多数[5]が同じ候補トランザクションのセットに合意するまで、提案との通信と更新を行います。

コンセンサスの間、各ノードは、選択されたバリデータと呼ばれる特定のピア群からの提案を評価します[6]。選択されたバリデータは、全体として考えたときに、提案を評価するノードを欺くために共謀しないための「信頼できる」ネットワークのサブセットを表します。この「信頼」の定義では、それぞれ個々の選択されたバリデータが信頼される必要はありません。むしろ、バリデータは、それらがネットワークに中継されたデータを改ざんする組織的な試みで共謀しないという期待に基づいて選択されます[7]。

図5: バリデータがトランザクション・セットを提案 — コンセンサスの開始時には、ノードは異なるトランザクションセットで動作する。提案のラウンドは、どのトランザクションがレジャーに適用され、どのトランザクションが次のコンセンサス・ラウンドを待つべきかを決定する。

 

合意済みの提案に含まれることに失敗した候補トランザクションは、引き続き候補トランザクションであり続けます。それらは次のコンセンサスのラウンドで再び検討されるかもしれません。

図6: コンセンサスを通じて、ノードはトランザクション・セットに合意する — ノードは、合意されたトランザクションのセット(緑色で表示)を最後に検証されたレジャーに適用する。そのセットに入っていないトランザクション(赤で表示)は、次のラウンドで合意される。

 

通常、1ラウンドのコンセンサスでパスしないトランザクションは、次のラウンドで成功します。しかし、状況によっては、トランザクションが無期限にコンセンサスを成立させることができない場合があります。そのような状況の1つは、ネットワークが基本料金(base fee)をそのトランザクションが提供するよりも高い値に増やす場合です。将来のある時点で手数料が引き下げられれば、そのトランザクションは成功する可能性があります。

Rippleは、そのようなトランザクションがいつまでも存続することを防止するメカニズムを提供し、トランザクション処理がタイムリーに行われることを保証します。アプリケーションは、各トランザクションに LastLedgerSequence パラメータを含める必要があります。これにより、指定されたレジャーシーケンス番号またはそれ以前に、トランザクションが成功または失敗することを保証します。こうして、アプリケーションが最終的なトランザクション結果を取得するまで待機する時間を制限します。

バリデーション(検証)

コンセンサスのラウンドが完了すると、各ノードは、コンセンサストランザクションセット内の候補トランザクションを最後に検証されたレジャーに適用することによって、新しいレジャーを算出する。

図7: ネットワーク・ノードはレジャー・バリデーションを計算する — 各トラッキングノードは、合意されたトランザクションを最後に検証されたレジャーに適用する。検証ノードは、それらの結果をネットワーク全体に送信する。

 

検証ノードは新しいバージョンのレジャーを計算し、それらの結果をネットワークに中継し、それぞれがコンセンサス中に提案された候補トランザクションに基づいて計算された著名付きハッシュを送信します。バリデーション(validations)と呼ばれるこれらの署名付きハッシュは、各ノードにそれぞれが計算したレジャーとそれらのピアが計算したレジャーを比較することを可能にします。

図8: ピアの大多数が同一の結果を算出したとき、レジャーは検証(validate)される — ノードは計算したレジャーと選択された検証ノードから受け取ったハッシュを比較する。一致しない場合、ノードは再計算するか、正しいレジャーを検索しなければならない。

 

ネットワークのノードは、ピアの大多数が同一のバリデーション・ハッシュに署名し、ブロードキャストしたときに、検証されたレジャー・インスタンスを認識します[8]。その後、トランザクションは、この更新され、今検証されたシーケンス番号 N+1 のレジャーに適用されます。

ノードが少数で、ピアとは異なるレジャーを算出した場合、そのノードはその算出したレジャーを無視します[9]。そのノードは正しいレジャーを再計算するか、必要に応じて正しいレジャーを取得します。

もし、ネットワークが検証において大多数の合意を達成できない場合、それは、一貫性のある提案を生成するコンセンサス・プロセスのために、トランザクション量が多すぎるか、またはネットワーク待ち時間が長すぎることを意味します。その場合、ノードはコンセンサス・プロセスを繰り返します。コンセンサスが始まってから時間が経過すると、各コンセンサス・ラウンドが符号を減らすため、ノードの大多数が同一の候補トランザクションのセットを受け取る可能性が次第に高くなるでしょう。Rippleネットワークは、これらの条件に応じて、手数料とコンセンサスを待つ時間を動的に調整します。

図9: ネットワークは新しい検証されたレジャーを認識する — コンセンサス・プロセスのラウンドの最後に、ノードは更新されたレジャーを持つ。

 

それらが検証上の大多数の合意に達すると、ノードはシーケンス番号 N+1 の新しい検証されたレジャーで作業します。その間に提出された新しいトランザクションとともに、前回のラウンドに含まれなかった候補トランザクションを考慮して、コンセンサスと検証のプロセスが繰り返されます[10]。

要点

Rippleネットワークに提出されたトランザクションは瞬時に処理されません。一定期間、各トランザクションは候補のままです。

単一トランザクションのライフサイクルは次のとおりです:

  1. トランザクションは、アカウント所有者によって作成され、署名されます。
  2. トランザクションはネットワークに提出されます。
    1. 間違って形成されたトランザクションは即座に拒否されることがあります。
    2. うまく形成されたトランザクションは暫定的に成功し、後で失敗する可能性があります。
    3. うまく形成されたトランザクションは一時的に失敗し、後で成功する可能性があります。
  3. コンセンサスの間、トランザクションはレジャーに含まれます。
    1. 成功したコンセンサス・ラウンドの結果は、検証されたレジャーです。
    2. コンセンサス・ラウンドが失敗した場合、コンセンサス・プロセスは成功するまで繰り返されます。
  4. 検証されたレジャーには、トランザクションとレジャー状態へのその影響が含まれます。

アプリケーションは、候補トランザクションの暫定的な結果ではなく、検証されたレジャーの情報のみを信頼すべきです。いくつかのRipple APIは、最初にトランザクションに対する仮の結果を返します。トランザクションの結果は、そのトランザクションが検証されたレジャーに含まれている場合、またはトランザクションに LastLedgerSequence が含まれ、そのシーケンス番号以下の検証されたレジャーに表示されない場合にのみ不変になります。

トランザクションを提出するアプリケーションのベストプラクティスは次のとおりです:

  1. トランザクションが確定的かつタイムリーに検証または失敗することを保証するために、LastLedgerSequence パラメーターを使用する。
  2. 検証されたレジャー内のトランザクション結果を確認する。
    1. トランザクションを含むレジャーが検証されるか、または LastLedgerSequence を経過するまで、結果は暫定的です。
    2. 結果コード tesSUCCESS と “validated”: “true” のトランザクションは、不変的に成功しています。
    3. 他の結果コードと “validated”: “true” のトランザクションは、不変的に失敗しています。
    4. トランザクションの LastLedgerSequence によって識別された検証済みのレジャーを含む、検証されたレジャーに表示されないトランザクションは、不変的に失敗しています。
      1. これを検出するために、連続したレジャー履歴を持つノードを使用するように注意してください[11]。
    5. LastLedgerSequence によって識別されるレジャーが検証されるまで、トランザクションの状態を繰り返し確認する必要があります。

その他のリソース


 

脚注

[1] – レジャー・インスタンスは、その内容のデジタルフィンガープリントであるハッシュによって一意に識別することもできます。

[2] – tec 結果コードのトランザクションはレジャーに含まれ、要求されたアクションは実行しません。これの根拠は、複数のトランザクションが順番に提出され、処理の順序はアカウントに関連付けられたシーケンス番号によって決定されることです(レジャーのシーケンス番号と混同しないでください)。後続のトランザクションをブロックするアカウントシーケンス番号の保留を防止するために、トランザクションはシーケンス番号を消費するように処理されます。さらに、ネットワークに配信されるトランザクションは、ネットワーク乱用を防止するために手数料を請求する必要があります。

[3] – 例えば、アリスが$ 100を持っていて、それをすべてボブに送るシナリオを考えてみましょう。アプリケーションが最初にその支払いトランザクションを提出し、直後にアリスの残高を確認すると、APIは $0 を返します。この値は、候補トランザクションの暫定結果に基づいています。支払が失敗し、アリスの残高が $100 になる(または、他のトランザクションが原因で、それ以外の金額になる)状況があります。アリスのボブへの支払いが成功したことを確実に知る唯一の方法は、トランザクションが検証されたレジャー内にあり、尚且つ結果コードが tesSUCCESS になるまで、トランザクションのステータスを確認することです。トランザクションが他の結果コードとともに検証されたレジャー内にある場合、その支払いは失敗しています。

[4] – 厳密に言えば、検証ノードはトラッキングノードのサブセットです。検証ノードは同じ機能を提供し、加えて「バリデーション」を作成します。トラッキングノードは、完全なレジャー履歴を保持しているか、部分的なレジャー履歴を保持しているかで更に分類することができます。

[5] – トランザクションは、トランザクションを認識したピアのパーセンテージがしきい値を下回った場合にコンセンサスを通過しません。各ラウンドは反復プロセスです。第1ラウンドの開始時には、少なくともピアの50%が同意しなければなりません。コンセンサスラウンドの最終的なしきい値は80%の合意です。これらの特定の値は変更される可能性があります。

[6] – UNL(Unique Node List)と呼ばれることもあります。

[7] – 選択されたバリデータから排他的にではなく、すべてのバリデータからの提案が評価された場合、悪意のある攻撃者は、無効なトランザクションを導入したり、提案から有効なトランザクションを削除したりするために、圧倒的多数を構成するために十分な検証ノードをスピンアップすることができます。選択されたバリデータリストはSybil攻撃を防ぎます。

[8] – 圧倒的多数の閾値は、2014年11月現在、少なくとも80%のピアがレジャーの検証に合意しなければならないことを要求しています。これは、コンセンサスのラウンドで要求されるのと同じパーセンテージになります。両方の閾値は変化する可能性があり、等しい必要はありません。

[9] – 実際には、ノードは、すべてのピアのバリデーションを受け取る前に、少数派であることを検出します。そのノードは、ピアの20%以上から一致しないバリデーションを受け取ったとき、そのバリデーションが80%の閾値を満たさないことを知ります。その時点で、レジャーの再計算を始めることができます。

[10] – 実際には、Rippleネットワークは、検証が完了する前に、新しいコンセンサスのラウンドを開始することによって、より効率的に実行されます。

[11] – Rippleノードはレジャーの完全な履歴がなくてもAPIリクエストに応答できます。サービスやネットワークの接続が中断すると、そのノードのレジャー履歴の中に、レジャーの抜けやギャップが発生することがあります。時間が経つと、もしそのように設定されていれば、rippledはその履歴のギャップを埋めます。欠落しているトランザクションをテストするときは、トランザクションが提出されてから LastLedgerSequence までの連続した完全なレジャーを持つノードに対して検証することが重要です。RPC server_state を使用して、特定のノードで使用可能な complete_ledgers を判別してください。


注意:

本ドキュメントは、リップル社が公式サイトで公開している『The Ripple Ledger Consensus Process』を私が個人的に翻訳したものです。注意して頂きたいのは、当方は現役のエンジニアではありませんし、Rippleについての技術的な探求心があるわけでもありません。誤訳などの間違いがあるかもしれないため、公式サイトの英語で書かれた原文もあわせて読むことをお勧めします。

コインチェック