BOLT #1 : Basis Protocol

BOLT #1 : Basis Protocol お知らせ
Table of Contents

Overview


このプロトコルは、個々のメッセージのフレーミングを処理する、基盤となる認証および順序付けされたトランスポートメカニズムを想定している。 BOLT#8 は、Lightning で使用される正規のトランスポート層を指定するが、上記の保証を満たす任意のトランスポートで置き換えることができる。(デフォルトの TCP ポート番号:9735、16進数:0x2607)

Connection Handling and Multiplexing


実装ではピアごとに単一の接続を使用しなければならない(チャンネルメッセージ(チャンネル ID を含む)はこの単一接続上を通して多重化してしている)。

Lightning Message Format


復号後、Lightning メッセージは以下のような形式になっている。

Table1

Field Size Description
type 2-byte メッセージのタイプ
payload variable メッセージタイプのフォーマットに適したメッセージ内容
extension ? オプションの TLV ストリーム

type フィールドは payload をどのように解釈するかを示している。個々のタイプのフォーマットはこのリポジトリの仕様書によって定義されている。タイプは「奇数ならばOK」というルールに従っているため、ノードは受信者の理解を確認せずに奇数タイプを送る可能性がある。メッセージは論理的に5つのグループに分けることができ、設定されている最上位ビットの順に並べられる。

Table2

Function Type-num Description
Setup & Control 0-31 接続セットアップ、制御、サポート機能、エラーレポートに関するメッセージ(後述)
Channel 32-127 マイクロペイメントチャネルのセットアップと削除用メッセージ(BOLT #2)
Commitment 128-255 現在のコミットメントトランザクションのアップデートに関連したメッセージ(手数料の更新と署名の交換と同様に HTLC の追加、廃止、清算を含む)(BOLT #2)
Routing 256-511 アクティブルートの探索、ノードとチャネルの通知を含むメッセージ(BOLT #7)
Custom 32768-65535 実験用またはアプリケーション専用メッセージ

メッセージの最大サイズはトランスポート層より 65535 bytes である。

送信ノード:

  • 事前の交渉なしに、ここに記載されていない均一にタイプされたメッセージを送信してはいけない。
  • 事前の交渉なしに、extension に偶数タイプの TLV レコードを送信してはいけない。
  • この仕様のオプションを交渉するとき
    • そのオプションで注釈が付けられたすべてのフィールドを含める必要がある
  • カスタムメッセージを定義するとき
    • 他のカスタムメッセージと衝突を避けるためにランダムな type を選択する
    • ここに記載されている他の実験と競合しない type を選択する
    • 通常のノードが追加データを無視する必要があるときは奇数 type 識別子を選択する
    • 通常のノードがメッセージを拒否して接続をクローズする必要があるときは偶数 type 識別子を選択する

受信ノード:

  • 知らない奇数タイプのメッセージを受け取ったとき:
    • 無視する
  • 知らない偶数タイプのメッセージを受け取ったとき:
    • 接続を切断する
    • チャネルに失敗する可能性がある
  • コンテンツの長さが不十分な知っているタイプのメッセージを受け取ったとき:
    • 接続を切断する
    • チャネルに失敗する可能性がある
  • extension ありのメッセージを受け取ったとき:
    • extension を無視する可能性がある
    • そうでなく、もし extension が無効である場合:
      • 接続を切断する
      • チャネルに失敗する可能性がある

Rationale

SHA2 と Bitcoin 公開鍵はデフォルトでビッグエンディアンであるため、他のフィールドで異なるエンディアンを使うのは一般的ではない。暗号学的ラッピングによって長さが 65535 bytes に制限されているため、プロトコルでのメッセージはいずれもその長さ以下である。

「奇数ならOK」というルールは将来のクライアントに交渉や特別なコーディングなしで任意の拡張を可能にする。拡張フィールドもまた同様に送信者が TLV データを追加することで将来の拡張を可能にする。

Type-Length-Value Format


プロトコル全体で TLV(Type-Length-Value)フォーマットは既存のメッセージタイプに下位互換性のある新しいフィールドを追加可能にするために使われる。

tlv_record は単一のフィールドを表し以下のフォームにエンコードされる。

  • [bigsize : type]
  • [bigsize : length]
  • [length : value]

tlv_stream は(ゼロの可能性もある)tlv_records のシリーズであり、エンコードされた tlv_records の連続として表されている。既存のメッセージを拡張するために使用するとき、tlv_stream は一般的には現在定義されている全てのフィールドの後に置かれる。

その type は BigSize フォーマットを使用してエンコードされている。それはメッセージ固有に機能し、tlv_record の 64-bit 識別子はコンテンツの値をどのようにデコードするべきかを決定している。2^16 以下の type 識別子はこの仕様書の中で予約されているため、2^16 以上の識別子をカスタムレコードに使用可能である。ここで定義されていないレコードはカスタムレコードとして見なされる。lengthvalue のサイズをバイト単位で通知する BigSize フォーマットを使用してエンコードされる。

Requirements

送信ノード:

  • 厳密に増加する type によって tlv_streamtlv_records を順序付けする必要があるため、同じ type の単一 TLV レコードを生成してはいけない
  • typelength を最小限にエンコードしなければならない
  • カスタムレコード type 識別子を定義するとき:
    • 他のカスタムタイプと衝突を避けるためにランダムな識別子を選ぶべきである
    • 一般ノードが追加データを無視する必要があるときは、奇数 type 識別子を使用する
    • 一般ノードがカスタムレコードに含まれる tlv_stream を拒否する必要があるとき、偶数 type 識別子を使用する
  • tlv_record において冗長で可変長なエンコーディングを使用すべきでない

受信ノード:

  • type を解析する前にゼロバイト残っている場合:
    • tlv_stream の解析を中止する
  • type もしくは length が最小にエンコードされていない場合:
    • tlv_stream は解析に失敗するに違いない
  • デコードされた type が厳格に増加していない(同じ type の2つ以上の存在が出会う状況を含む)場合:
    • tlv_stream は解析に失敗するに違いない
  • length がメッセージの残りのバイト数を超える場合:
    • tlv_stream は解析に失敗するに違いない
  • 知っている type の場合:
    • type の既知のエンコーディングを使用して次の length バイトをデコードする
    • lengthtype の既知のエンコーディングに必要な長さと等しくない場合:
      • tlv_stream は解析に失敗するに違いない
    • type の既知のエンコーディング内の可変長フィールドが最小でない場合:
      • tlv_stream は解析に失敗するに違いない
  • 知らない type の場合:
    • type が偶数の場合:
      • tlv_stream は解析に失敗するに違いない
    • type が奇数の場合:
      • 次の length バイトを捨てる必要がある

Rationale

TLV を使用する主な利点は、各フィールドはエンコードされた要素のぴったりのサイズを運ぶため読み手が知らない新しいフィールドを無視することができることである。TLV がない場合、ノードが特定のフィールドを使用したくない場合でも、ノードは後続のフィールドのオフセットを決定するために、そのフィールドの解析ロジックを追加する必要がある。

厳格な単調性制約は全ての type は一意であり最大で一度だけ現れることを保証する。複雑なオブジェクトにマップするフィールド(ベクトル、マップ、構造体など)は、オブジェクトが単一の tlv_record 内でシリアル化されるようにエンコーディングを定義することにより、そうする必要がある。 一意性制約は、以下の最適化を可能にする。

  • 正規の順序は、エンコードされた値とは無関係に定義される
  • 正規の順序は、エンコーディングの時に動的に決定されるのではなく、コンパイル時に知ることができる
  • 正規の順序を検証することは必要な状態が少なく、コストが少なくなる
  • 可変サイズのフィールドでは、要素を順番に追加してダブルアンドコピーのオーバーヘッドを発生させるのではなく、事前に予想サイズを予約できる

大きいサイズの typelength の使用は小さい type や短い value の空間を節約することが可能になる。これにより、回線上または onion ペイロードにアプリケーションデータ用のより多くのスペースが残る可能性がある。

全ての type は、根本にある tlv_record の正規エンコーディングを作成するために昇順に表示する必要がある。これは、検証者が署名者のように同じメッセージダイジェストを再計算できることを保証するので、tlv_stream で署名を計算する時に極めて重要である。

書き手は長さを2回計算することになり、外側の長さの計算が複雑にならないようにするため tlv_record において冗長で可変長なエンコーディングの使用をさけるべきである。例えば、可変長のバイト列を書く時、tlv_record はすでに後続のバイト数を持っているため、その値は生バイトのみに含まれており追加の内部長は含まれていない。つまり、tlv_record が複数の可変長要素が含まれていると、これは冗長とは見なされず value から個々の要素を解析することを受け手に許可する必要がある。

Fundamental Types


様々な基本的タイプはメッセージ仕様書において言及される:

  • byte: 8-bit バイト
  • u16: 2 バイト符号なし整数
  • u32: 4 バイト符号なし整数
  • u64: 8 バイト符号なし整数

単一の値を含む TLV レコード内では、整数の先行ゼロを省略できる:

  • tu16: 0 to 2 バイト符号なし整数
  • tu32: 0 to 4 バイト符号なし整数
  • tu64: 0 to 8 バイト符号なし整数

以下の便利なタイプも定義されている:

  • chain_hash: 32 バイトチェーン識別子 (BOLT #0)
  • channel_id: 32 バイトチャネル識別子 (BOLT #2)
  • sha256: 32 バイト SHA2-256 ハッシュ値
  • signature: 64 バイト bitcoin 楕円曲線署名
  • point: 33 バイト楕円曲線ポイント (SEC 1標準に準拠した圧縮エンコーディング)
  • short_channel_id: 8 バイトチャネル識別子 (BOLT #7)
  • bigsize: Bitcoin's CompactSize エンコーディングと同様に可変長で符号なし整数(Appendix : A)

Setup Messages


The init Message

一度認証が完了すると最初のメッセージは、たとえこれが再接続であってもこのノードによってサポートされているもしくは要求されている特徴を明らかにする。BOLT #9 は特徴のリストを記している。各特徴は一般的に 2 bits で表されており、最下位ビットの番号は0(偶数)で、次の最上位ビットの番号は1(奇数)である。歴史的理由によって、特徴はグローバルとローカルビットマスクに分けられる。

features はバイトまでゼロでパッディングされなければならない。

  • type: 16 (init)
  • data:
    • [u16:gflen]
    • [gflen*byte:globalfeatures]
    • [u16:flen]
    • [flen*byte:features]
    • [init_tlvs:tlvs]
  • tlvs: init_tlvs
  • types:
    • type: 1 (networks)
    • data:
      • [...*chain_hash:chains]

任意の networks はそのノードが挿入されているチェーンを示している。

Requirements

送信ノード:

  • どの接続においても最初の Lightning メッセージとして init を送らなければならない
  • BOLT #9 で定義されている features ビットをセットしなければならない
  • 未定義の機能ビットをゼロに設定する必要がある
  • globalfeatures において 13 よりも大きな features をセットするべきではない
  • feature フィールドを示すために必要な最短の長さを使うべきである
  • ゴシップするもしくはオープンなチャネルの全てに networks をセットすべきである

受信ノード:

  • 他のメッセージを送る前に init メッセージを受け取るのを待つ必要がある
  • 2つの特徴ビットマップを1つの論理 feature マップに結合させる必要がある
  • BOLT #9 で述べられた知っている feature に応答する必要がある
  • 非ゼロの知らない奇数特徴ビットを受け取ったとき:
    • そのビットを無視する
  • 非ゼロの知らない偶数特徴ビットを受け取ったとき:
    • 接続を失敗させる必要がある
  • 非一般チェーンに含まれる networks を受け取ったとき:
    • 接続が失敗する可能性がある
  • 特徴ベクトルがすべての既知の推移的な依存関係を設定しない場合:
    • 接続を失敗させる必要がある

Rationale

ここでは2つの機能ビットフィールドが使われているが、下位互換性のためにそれらは1つに結合されている。このセマンティクスにより、将来の互換性のない変更と下位互換性のある変更の両方が可能になる。オプションの機能が後で強制的になるようにするために、ビットは通常ペアで割り当てる必要がある。ノードは特徴に互換性がないとき、エラー診断書を単純化するために他の特徴の受信を待つ。

(ほとんどの実装では単一のネットワークしかサポートしていないが)全ての networks で同じポートを共有しているため、その networks フィールドは、ノードが優先ネットワークに関する更新を受信する、またはチャネルを開くことができると誤って信じるノードを回避する。

The error Message

診断の簡素化のために、何かが間違っていることをピアに教えることはたいてい便利である。

  • type: 17 (error)
  • data:

    • [channel_id : channel_id]
    • [u16 : len]
    • [len*byte : data]

    2 バイトの len フィールドはすぐ後に続くバイト数を示している。

Requirements

チャネルは channel_id で参照され、channel_id が 0 のときは全てのチャネルを参照する。

資金調達ノード:

  • funding_created メッセージの前に送られた全てのエラーメッセージ:
    • MUST use temporary_channel_id in lieu of channel_id.
    • channel_id の代わりに temporary_channel_id を使用する必要がある

fundee ノード:

  • funding_signed メッセージの前に送られた全てのエラーメッセージ:
    • channel_id の代わりに temporary_channel_id を使用する必要がある

送信ノード:

  • error を送信するとき:
    • エラーメッセージによって参照されたチャネルを失敗させる必要がある
  • プロトコル違反や、チャネルを使用不可にしたり通信を不可能にする内部エラーに error を送る必要がある
  • 不明なチャネルに関連した 32-255 タイプのメッセージへの応答して、不明なチャネルでエラーを送信する必要がある
  • 空の data フィールドを送る可能性がある
  • 失敗が正当でない署名検証で起きた場合:
    • funding_created, funding_signed, closing_signed, commitment_signed メッセージの応答に 16 進数でエンコードされた生トランザクションを入れる必要がる
  • channel_id がゼロの場合:
    • 受信ノードですべてのチャネルを失敗させる必要があります
    • 接続を切断する必要がある
  • data と等しい len をセットする必要がある

受信ノード:

  • error を受信したとき:
    • MUST fail the channel referred to by the error message, if that channel is with the sending node.
    • エラーメッセージによって参照されたチャネルが送信者とのチャネルならば、そのチャネルを失敗させる必要がある
  • メッセージによって存在しないチャネルを参照した場合:
    • そのメッセージを無視する
  • lenをパケットの残りに切り詰める必要がある(大きい場合)
  • data が印刷可能な ASCII 文字列のみで構成されていない場合:
    • 言葉通りに data を印字すべきではない

Rationale

会話を中止することを要求する回復不可能なエラーがある。もし、接続が単に落ちただけならば、もう一度接続を試みれば良い。

Control Messages


The ping and pong Messages

長期間の TCP 接続の存在を許可するために、両者のアプリケーションレベルで TCP 接続の延命を要求される場合がある。そのようなメッセージはトラフィックパターンの難解化もすることができる。

  • type: 18 (ping)
  • data:
    • [u16 : num_pong_bytes]
    • [u16 : byteslen]
    • [byteslen*byte : ignored]

pong メッセージは ping メッセージを受け取るたびに送られる。それは、その受信者がまだアクティブであることを他者に明確に知らせる一方で、応答としても接続の延命としても提供される。受け取った ping メッセージ内で、送信者は pong メッセージのデータペイロードに含まれるバイト数を明確に指定する。

  • type: 19 (pong)
  • data:
    • [u16 : byteslen]
    • [byteslen*byte : ignored]

Requirements

ping メッセージを送るノード:

  • ignored にゼロをセットする
  • ignored に初期化されたメモリの秘密または部分のようなセンシティブなデータをセットしてはいけない
  • 一致する pong を受け取らなかった場合:
    • ネットワーク接続を終了する場合がある(チャネルを失敗させてはいけない)
  • 30秒よりも短い間隔で ping メッセージを送るべきではない

pong メッセージを送るノード:

  • ignored にゼロをセットする
  • ignored に初期化されたメモリの秘密または部分のようなセンシティブなデータをセットしてはいけない

ping メッセージを受け取るノード:

  • 30秒に1回を超える ping を大幅に受信した場合は、チャネルを失敗させる必要がある
  • num_pong_bytes が 65532 よりも小さい場合:
    • pong メッセージを送ることで応答する必要がある(bytelennum_pong_bytes と同じにする)
  • それ以外 (num_pong_bytes > 65532):
    • ping を無視する

pong メッセージを受け取るノード

  • byteslen が送られた pingnum_pong_bytes 値と一致しない場合:
    • チャネルが失敗する場合がある

Rationale

可能な最大のメッセージは 65535 バイトである。 したがって、タイプフィールド(pong)と byteslen 自体を考慮するために、最大の感知可能な byteslen は 65531 である。これにより、num_pong_bytes の便利なカットオフにより、応答を送信しないように指示できる。ペイメントチャネルが無制限の寿命を持っているので、ネットワーク内におけるノード間の接続は長く続く可能性がある。しかし、接続寿命の相当の時間で新しいデータが交換されない可能性が高い。また、様々なプラットフォームにおいて Lightning クライアントは事前の警告なしでスリープの状態になる可能性がある。従って、確立された接続をアクティブに保つだけでなく、反対側の接続の活性を調査するために、別個の ping メッセージが使用されます。

加えて、送信者が受信者に特定のバイト数の応答を送信するように要求する機能により、ネットワーク上のノードは合成トラフィックを作成できる。 このようなトラフィックは、ノードは、それぞれのチャネルに真の更新を適用せずに、通常の交換のトラフィックパターンを偽造できるため、パケットおよびタイミング分析から部分的に防御するために使用できる。BOLT #4 で定義されている onion ルーティングプロトコルと組み合わせると、注意深く統計的に駆動される合成トラフィックが、ネットワーク内の参加者のプライバシーをさらに強化するのに役立つ。制限された予防策は ping フラッディングに対して推奨されている。しかし、ネットワーク遅延によって受け取っている地域もいくつかある。着信トラフィックのフラッディングには他の方法があることに注意されたい。(例:奇数の不明メッセージタイプを送る、メッセージにパディングを行うなど)

最後に、定期的な ping メッセージの用法は BOLT#8 で指定されているように、頻繁なキーローテーションを促進するのに役立つ。

タイトルとURLをコピーしました