このプロトコルは、個々のメッセージのフレーミングを処理する、基盤となる認証および順序付けされたトランスポートメカニズムを想定している。 BOLT#8 は、Lightning で使用される正規のトランスポート層を指定するが、上記の保証を満たす任意のトランスポートで置き換えることができる。(デフォルトの TCP ポート番号:9735、16進数:0x2607)
実装ではピアごとに単一の接続を使用しなければならない(チャンネルメッセージ(チャンネル ID を含む)はこの単一接続上を通して多重化してしている)。
復号後、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
が無効である場合:SHA2
と Bitcoin 公開鍵はデフォルトでビッグエンディアンであるため、他のフィールドで異なるエンディアンを使うのは一般的ではない。暗号学的ラッピングによって長さが 65535 bytes に制限されているため、プロトコルでのメッセージはいずれもその長さ以下である。
「奇数ならOK」というルールは将来のクライアントに交渉や特別なコーディングなしで任意の拡張を可能にする。拡張フィールドもまた同様に送信者が TLV データを追加することで将来の拡張を可能にする。
プロトコル全体で TLV(Typr-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 以上の識別子をカスタムレコードに使用可能である。ここで定義されていないレコードはカスタムレコードとして見なされる。length
は value
のサイズをバイト単位で通知する BigSize フォーマットを使用してエンコードされる。
送信ノード:
type
によって tlv_stream
の tlv_records
を順序付けする必要があるため、同じ type
の単一 TLV レコードを生成してはいけないtype
と length
を最小限にエンコードしなければならない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
バイトをデコードするlength
が type
の既知のエンコーディングに必要な長さと等しくない場合:tlv_stream
は解析に失敗するに違いないtype
の既知のエンコーディング内の可変長フィールドが最小でない場合:tlv_stream
は解析に失敗するに違いないtype
の場合:type
が偶数の場合:tlv_stream
は解析に失敗するに違いないtype
が奇数の場合:length
バイトを捨てる必要があるTLV を使用する主な利点は、各フィールドはエンコードされた要素のぴったりのサイズを運ぶため読み手が知らない新しいフィールドを無視することができることである。TLV がない場合、ノードが特定のフィールドを使用したくない場合でも、ノードは後続のフィールドのオフセットを決定するために、そのフィールドの解析ロジックを追加する必要がある。
厳格な単調性制約は全ての type
は一意であり最大で一度だけ現れることを保証する。複雑なオブジェクトにマップするフィールド(ベクトル、マップ、構造体など)は、オブジェクトが単一の tlv_record
内でシリアル化されるようにエンコーディングを定義することにより、そうする必要がある。 一意性制約は、以下の最適化を可能にする。
大きいサイズの type
と length
の使用は小さい type
や短い value
の空間を節約することが可能になる。これにより、回線上または onion ペイロードにアプリケーションデータ用のより多くのスペースが残る可能性がある。
全ての type
は、根本にある tlv_record
の正規エンコーディングを作成するために昇順に表示する必要がある。これは、検証者が署名者のように同じメッセージダイジェストを再計算できることを保証するので、tlv_stream
で署名を計算する時に極めて重要である。
書き手は長さを2回計算することになり、外側の長さの計算が複雑にならないようにするため tlv_record
において冗長で可変長なエンコーディングの使用をさけるべきである。例えば、可変長のバイト列を書く時、tlv_record
はすでに後続のバイト数を持っているため、その値は生バイトのみに含まれており追加の内部長は含まれていない。つまり、tlv_record
が複数の可変長要素が含まれていると、これは冗長とは見なされず value
から個々の要素を解析することを受け手に許可する必要がある。
様々な基本的タイプはメッセージ仕様書において言及される:
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)init
Message一度認証が完了すると最初のメッセージは、たとえこれが再接続であってもこのノードによってサポートされているもしくは要求されている特徴を明らかにする。BOLT #9 は特徴のリストを記している。各特徴は一般的に 2 bits で表されており、最下位ビットの番号は0(偶数)で、次の最上位ビットの番号は1(奇数)である。歴史的理由によって、特徴はグローバルとローカルビットマスクに分けられる。
features
はバイトまでゼロでパッディングされなければならない。
init
)u16
:gflen
]gflen*byte
:globalfeatures
]u16
:flen
]flen*byte
:features
]init_tlvs
:tlvs
]init_tlvs
networks
)...*chain_hash
:chains
]任意の networks
はそのノードが挿入されているチェーンを示している。
送信ノード:
init
を送らなければならないfeatures
ビットをセットしなければならないglobalfeatures
において 13 よりも大きな features
をセットするべきではないfeature
フィールドを示すために必要な最短の長さを使うべきであるnetworks
をセットすべきである受信ノード:
init
メッセージを受け取るのを待つ必要があるfeature
マップに結合させる必要があるfeature
に応答する必要があるnetworks
を受け取ったとき:ここでは2つの機能ビットフィールドが使われているが、下位互換性のためにそれらは1つに結合されている。このセマンティクスにより、将来の互換性のない変更と下位互換性のある変更の両方が可能になる。オプションの機能が後で強制的になるようにするために、ビットは通常ペアで割り当てる必要がある。ノードは特徴に互換性がないとき、エラー診断書を単純化するために他の特徴の受信を待つ。
(ほとんどの実装では単一のネットワークしかサポートしていないが)全ての networks
で同じポートを共有しているため、その networks
フィールドは、ノードが優先ネットワークに関する更新を受信する、またはチャネルを開くことができると誤って信じるノードを回避する。
error
Message診断の簡素化のために、何かが間違っていることをピアに教えることはたいてい便利である。
type: 17 (error
)
data:
channel_id
: channel_id
]u16
: len
]len*byte
: data
]2 バイトの len
フィールドはすぐ後に続くバイト数を示している。
チャネルは channel_id
で参照され、channel_id
が 0 のときは全てのチャネルを参照する。
資金調達ノード:
funding_created
メッセージの前に送られた全てのエラーメッセージ:channel_id
の代わりに temporary_channel_id
を使用する必要があるfundee ノード:
funding_signed
メッセージの前に送られた全てのエラーメッセージ:channel_id
の代わりに temporary_channel_id
を使用する必要がある送信ノード:
error
を送信するとき:error
を送る必要があるdata
フィールドを送る可能性があるfunding_created
, funding_signed
, closing_signed
, commitment_signed
メッセージの応答に 16 進数でエンコードされた生トランザクションを入れる必要がるchannel_id
がゼロの場合:data
と等しい len
をセットする必要がある受信ノード:
error
を受信したとき:len
をパケットの残りに切り詰める必要がある(大きい場合)data
が印刷可能な ASCII 文字列のみで構成されていない場合:data
を印字すべきではない会話を中止することを要求する回復不可能なエラーがある。もし、接続が単に落ちただけならば、もう一度接続を試みれば良い。
ping
and pong
Messages長期間の TCP 接続の存在を許可するために、両者のアプリケーションレベルで TCP 接続の延命を要求される場合がある。そのようなメッセージはトラフィックパターンの難解化もすることができる。
ping
)u16
: num_pong_bytes
]u16
: byteslen
]byteslen*byte
: ignored
]pong
メッセージは ping
メッセージを受け取るたびに送られる。それは、その受信者がまだアクティブであることを他者に明確に知らせる一方で、応答としても接続の延命としても提供される。受け取った ping
メッセージ内で、送信者は pong
メッセージのデータペイロードに含まれるバイト数を明確に指定する。
pong
)u16
: byteslen
]byteslen*byte
: ignored
]ping
メッセージを送るノード:
ignored
にゼロをセットするignored
に初期化されたメモリの秘密または部分のようなセンシティブなデータをセットしてはいけないpong
を受け取らなかった場合:ping
メッセージを送るべきではないpong
メッセージを送るノード:
ignored
にゼロをセットするignored
に初期化されたメモリの秘密または部分のようなセンシティブなデータをセットしてはいけないping
メッセージを受け取るノード:
ping
を大幅に受信した場合は、チャネルを失敗させる必要があるnum_pong_bytes
が 65532 よりも小さい場合:pong
メッセージを送ることで応答する必要がある(bytelen
を num_pong_bytes
と同じにする)num_pong_bytes
> 65532):ping
を無視するpong
メッセージを受け取るノード
byteslen
が送られた ping
の num_pong_bytes
値と一致しない場合:可能な最大のメッセージは 65535 バイトである。 したがって、タイプフィールド(pong
)と byteslen
自体を考慮するために、最大の感知可能な byteslen
は 65531 である。これにより、num_pong_bytes
の便利なカットオフにより、応答を送信しないように指示できる。ペイメントチャネルが無制限の寿命を持っているので、ネットワーク内におけるノード間の接続は長く続く可能性がある。しかし、接続寿命の相当の時間で新しいデータが交換されない可能性が高い。また、様々なプラットフォームにおいて Lightning クライアントは事前の警告なしでスリープの状態になる可能性がある。従って、確立された接続をアクティブに保つだけでなく、反対側の接続の活性を調査するために、別個の ping
メッセージが使用されます。
加えて、送信者が受信者に特定のバイト数の応答を送信するように要求する機能により、ネットワーク上のノードは合成トラフィックを作成できる。 このようなトラフィックは、ノードは、それぞれのチャネルに真の更新を適用せずに、通常の交換のトラフィックパターンを偽造できるため、パケットおよびタイミング分析から部分的に防御するために使用できる。BOLT #4 で定義されている onion ルーティングプロトコルと組み合わせると、注意深く統計的に駆動される合成トラフィックが、ネットワーク内の参加者のプライバシーをさらに強化するのに役立つ。制限された予防策は ping
フラッディングに対して推奨されている。しかし、ネットワーク遅延によって受け取っている地域もいくつかある。着信トラフィックのフラッディングには他の方法があることに注意されたい。(例:奇数の不明メッセージタイプを送る、メッセージにパディングを行うなど)
最後に、定期的な ping
メッセージの用法は BOLT#8 で指定されているように、頻繁なキーローテーションを促進するのに役立つ。