[LN#010]送金 (4)

今回はcommitment transactionの構成について説明する。
トランザクションの構成は、BOLT3に記載されている。

  • version : 2
  • locktime : obscured commitment transaction number
  • txin : 1
  • txout : n

トランザクションのINPUTは1つで、funding transactionである。
OUTPUTは、状況によって異なる。

自分が展開できるcommitment transactionを眺めた場合、fundingした資金のうちの自分の取り分と相手の取り分がある。
これがそれぞれ、to_local outputto_remote outputになる。
それ以外には、HTLCがある。
自分から相手に送金したOffered HTLC outputsと、相手から自分に送金されたReceived HTLC outputsである。


to_remote outputはP2WPKHへの送金なので、commitment transactionを展開するとすぐに相手が使用できるが、それ以外はスクリプトへの送金になるため、スクリプトを解けなくては使用できない。


to_local outputのスクリプトは自分が持つ鍵情報だけで解くことができるのだが、Establish Channel時に相手からもらった to_self_delay を組み込むことになっている。

image

例えば、to_self_delayが40だった場合、to_local outputを解くためにはcommitment transactionが展開されてから40ブロック以上待たなくてはならない。

もし、<revocationkey>(公開鍵)の秘密鍵revocationsecretkeyを持っているならば、それで署名してOP_IFのルートでスクリプトを解くことができ、その場合は待ち時間はない。
ただ、revocationsecretkeyの計算にはそのスクリプトを展開した人のper_commitment_secretという情報と、相手の人のrevocation_basepoint_secretという情報が必要になっている。
per_commitment_secretは、revoke_and_ackメッセージで過去の分を交換するが、revocation_basepoint_secretについては交換しない。
そのため、revocationsecretkeyはcommitment transactionを展開した人は作成できないが、その相手は作成できることになる(ただし、過去にrevoke_and_ackメッセージで交換したものに限る)。

こうすることで、過去のcommitment transactionを展開すると相手に奪い取られるようになっている。


Offered HTLC outputsは、支払った側が作るoutputである。
つまり、update_add_htlcメッセージを送信した人はcommitment transactionにOffered HTLC outputを追加していく。

image

スクリプトの作りが複雑だが、通常は5行目のOP_ELSE内の方が処理される(最初のOP_IFは、廃棄したcommitment transactionを展開されたときのルート)。

自分がcommitment transactionを展開した場合は7行目(A)のOP_NOTIFのルート、そうでない場合は10行目(B)のOP_ELSEのルートになる。
7行目(A)を通る場合はHTLC-Timeout transactionへの出力、10行目(B)を通る場合はpayment_preimageを持っている場合だけ解くことができる。

 

送金した人(=Offered HTLC outputsを持つ)がcommitment transactionを展開したとする。
そのHTLCは、通常は相手が受け取るはずで、受け取るときにはpayment preimageという領収書のIDが必要になる(送金(2)参照)。これはBのルート。
もし相手が受け取らないままタイムアウトを迎えた場合は、送金した人が取り戻す。こちらはAのルートである。

同じ”Offered HTLC outputs”という名称でも、どちらがcommitment transactionを展開したかによって見方が変わることに注意していただきたい。


Received HTLC outputsはその逆で、相手からupdate_add_htlcメッセージを受信した人が自分のcommitment transactionに追加することになる。

image

Offered HTLC outputsと似て、通常は5行目のOP_ELSE内を通る。
8行目(C)のOP_IF内は自分がcommitment transactionを展開し、payment preimageを持っている場合のルート。
12行目(D)のOP_ELSE内は相手がcomitment transactionを展開したときのルート。

 

着金した人(=Received HTLC outputsを持つ)がcommitment transactionを展開したとする。
そのHTLCは通常は自分が受け取るはずで、それにはpayment preimageが必要になる(Cのルート)。
もし自分がpreimageを取得できないままタイムアウトした場合は、相手が取り戻す(Dのルート)。


これらのoutputは、送金額が決められた値以下の場合は取り除かれることになっている。
https://github.com/lightningnetwork/lightning-rfc/blob/master/03-transactions.md#trimmed-outputs

通常のBitcoinであれば546satoshisだが、それとは別にopen_channelaccept_channelのdust_limit_satoshisで指定できる。
指定できるといっても、最終的にはBitcoinのブロックチェーンに展開することになるので、Bitcoinの制約を下回ることはできない。


commitment transactionについては、ここまでとする。
スクリプトが複雑なので、興味がある方は動きを確認することをお勧めする。

commitment transaction自体にはP2WPKH/P2WSHだけしか見えず、実際にスクリプトがトランザクションに現れるのは送金先トランザクションである。
次回は、その送金先トランザクションについて説明していく。

[LN#009]送金 (3)

前回、2ノード間での送金メッセージについて説明した。
以降は、送金にまつわるデータについて紹介していく。

今回は、commitment transactionについて解説する。


2ノード間でチャネルを解説するときにfunding transaction をブロックチェーンに展開した。
BOLTでは、その出力のcommitment transactionというトランザクションとHTLCで送金を管理している。

funding transactionの出力は、commitment transactionか、お互いが同意してチャネルを閉鎖するときのclosing transactionかのいずれかである。

image

同意してチャネルを閉鎖することをMutual Closeと呼ぶ。
正常に閉鎖する場合は、手数料や送金結果を最短でしようできるようになるので、Mutual Closeしてclosing transaction をブロックチェーンに展開するのがお互いにとってよい。

ただ、チャネルを解説している間に何か問題が発生して、Mutual Close の手続きができない状態になるかもしれない。その場合でも最新のcommitment transaction を展開すれば、取り戻すことができる。
そういうチャネル閉鎖のやり方を、Unilateral Closeと呼ぶ。

金額が変化するとcommitment transactionの構成が変化するので、お互いが正しいcommitment transaction を持っていることを確認するためにcommitment_signed メッセージで署名を送り合っている。
もし1satoshiでも違ったり、トランザクションのvoutの順番が違っていたりすると、署名が異なることになる。


funding transactionは2-of-2のMultiSig へ送金しているため、commitment transaction には双方が署名する必要がある。
BOLTメッセージではトランザクション本体を送りあうことはないので、署名をするのに必要なだけの情報をお互いが持っていなくてはならない。

commitment_signedでは相手にcommitment transaction の署名を送るが、これは「相手が持つcommitment transaction に自分が行った署名」を渡している。
つまり、自分では「自分が送信できるcommitment transaction」と「相手が送信するためのcommitment transaction」のデータを持ち、メッセージでは相手のための署名だけを送っていることになる。

image

自分の秘密鍵を相手に渡すことはないので、相手用のcommitment transaction に署名まではできるのだが、相手がそれに署名した結果をもらうことはないため、相手用のcommitment transaction は自分ではブロックチェーンに展開できないのである。


今回はcommitment transactionの位置づけと、署名のされ方について解説した。
次回はcommitment transactionの構成について説明する。

[LN#008]送金 (2)

送金の解説は、メッセージ交換についてと、交換するデータについてに分けて行う。
データについて把握しないとメッセージ交換だけ見てもわからない箇所が出てきてしまうが、並列して説明すると煩雑になってしまうため、順番に見ていこう。


2ノード間で送金を行った場合のシーケンスを以下に示す。

image

ここに出てくるメッセージは4種類である。

前回の解説で、「送金する情報(HTLC)の追加」と「送金を実行」に分かれると書いたが、具体的には「update_add_htlcを送信して確定するまで」と「update_fulfill_htlcを送信して確定するまで」に分かれている。


最初のメッセージはupdate_add_htlcから始まるが、その前に送金元からinvoiceを入手する必要がある。

最低限必要な情報は、以下である。

  • 領収書IDのハッシュ値
  • 請求額

領収書IDはpayment_preimage、そのハッシュ値はpayment_hashと呼んでいる。
ハッシュ演算は、SHA256である。
また、請求額の単位はmsatである。

この情報を表現するための仕様がBOLT11だが、上記データを相手に渡すことさえできればupdate_add_htlcメッセージを作成することができる。
渡すためのBOLTメッセージはないため、それ以外のルート(メールやQRコードなど)を使うことになるだろう。


update_add_htlcメッセージでは、payment_hashやamount_msat(請求額)を送信するが、それ以外に重要な情報として「cltv_expiry」と「onion_routing_packet」がある。

cltv_expiryは、OP_CHECKLOCKTIMEVERIFYというBitcoinスクリプトの命令で使う値で、スクリプトの制限時間を決めるために使用する。
update_add_htlcを送信する=相手に送金をする、ということになるため、通常はそのまま受けとってもらいたいのだが、HTLCを追加してupdate_fulfill_htlcを送金するまでの間に何か問題があり、相手のPCが故障などしていなくなるかもしれない。
その場合でもHTLCに手を出せないままにならないよう、一定期間以上経っても相手が受け取らない場合は、タイムアウトして取り戻すことが可能になっている。

onion_routing_packetは、送金するルートを指定した情報である。
今回は2ノード間のためルートは1つしかないが、BOLTの送金は転送を繰り返して相手に届けることが可能になっているため、どのノードを経由して送金するかを送金元が決定できるようになっている。
中継するノードは、多少の手数料を取ることができるため、最終的に安いルートを使いたいかもしれないし、信用が高いノードがあるのであればそこを経由させたいかもしれない。

名前に「onion_」とついているのは、ルート情報はノードをそのまま載せているわけではなく、受信した人はその次に転送する相手のノードしかわからないようになっているためである。
そのデータの作り方が、1つノード情報を入れたら暗号化、それに次のノード情報を追加して暗号化、というように、1回ずつ暗号化させてデータを作っていき、受信した人は1つだけ解読して次のノード情報を読み、また次の人はノード情報を読み・・・と繰り返していく。
それが、タマネギの皮をはいでいくような様子に見えるので、onion routing protocolと呼ばれている(のだと思う)。
onion routing protocolはBOLT4にて説明されている。サンプル実装はこちらである。


update_add_htlcを受信したノードは、payment_hashが自分のpayment_preimageのハッシュ値と一致するかを確認し、一致しているのであればamount_msatとcltv_expiryを期待値と一致するか確認する。

それで問題が無ければ、update_fulfill_htlcメッセージを送信する。
このときに、payment_preimageを返す。
2ノード間では、payment_preimageはpayment_hashのチェックにしか使用しないが、転送している場合には重要な意味を持つ(説明は送金の転送で行う)。


update_add_htlcとupdate_fulfill_htlcの後に送信し合うcommitment_signedとrevoke_and_ackについて説明する。

 

commitment_signedは、funding transactionからの出力であるcommitment transactionの署名と、各HTLCについての署名を送信するメッセージである。

commitment transactionはfunderとfundeeのMultiSigなので、それぞれが署名しないと送金できないため、commtiment transactionの内容が変わるたびに署名を送りあい、いつでもブロックチェーンに展開できるようにしている。

 

commitment transactionに署名してしまうと展開できるようになるが、こちらに送金してもらった後に相手が送金前のcommitment transactionを展開されてしまうと、自分への送金がなかったことになってしまう。
そういう裏切り行為をされないように、古いcommitment transactionを展開すると損をするようなスクリプトをcommitment transactionに埋め込んである。
その、今までのcommitment transactionを過去のものとして確定するためのメッセージが、revoke_and_ackである。

revoke_and_ackでは、per_commitment_secretという今まで使っていたper_commitment_pointの秘密鍵にあたるデータを相手に送信する。
このデータがあると、それによって作られたcommitment transactionでは、payment_preimageがなくてもHTLCを取得できるし、相手の配分になっていた額も取得できるようになっている。

Bitcoinのしくみ上、正しいトランザクションを展開することを止めることはできないが、展開すると損をするという制約をつけることで、不正をさせる抑止力にしようとしている。


基本的なメッセージ交換は以上である。
次回からは、データについて見ていく。

[LN#007]送金 (1)

ここからは、2ノード間での送金処理について解説していく。

送金は大きく、「送金する情報(HTLC)の追加(1~5)」と「送金を実行(6~8)」の2段階に分かれている。

  1. 送金先:請求書(=送金先であることを証明するIDのハッシュ値)を作成し、送金元に渡す。
  2. 送金元:送金先までのルートを決定する。
  3. 送金元:送金ルートの直近ノードに、ルート情報とHTLCを追加するメッセージを送信する。
  4. 中継:送金する情報を追加するメッセージを受信した場合、自分が送金元で無い場合はルート情報を読んで次のノードに転送する。
  5. 各ノード:HTLCを追加するメッセージが正しければ、HTLCを受け取った場合のメッセージ交換を行う。
  6. 送金先:HTLCメッセージの中にIDのハッシュ値があるので、それが請求書のハッシュ値と一致することを確認する。
  7. 送金先:自分の請求書IDを相手に返信して、送金を完了させることを宣言する。
  8. 各ノード:受け取った請求書IDを相手に返信していく。

(※上記の番号と図中の番号は対応していません)
image

 

HTLCはHashed TimeLock Contractsの略だが、ここでこの言葉を使う場合は、送金する情報だったり、送金を使用するためのスクリプトだったりするので、あまり用語に縛られず、そういったものを表していると考えていただきたい。

このHTLCは、相手に渡す「Offered HTLC outputs」と、相手からもらう「Received HTLC outputs」がある。
“outputs”とついているのは、これがcommitment transactionの送金先になるからである。

これまでの章では、送金を「出資額を分割した比率が変わるだけ」と説明してきたが、途中での通信切断や、相手が不正を働いたときのために、送金者から送金したい額を引き、送金額をHTLC outputとして追加する。
もし、送金の途中で問題が起きたとしても、送金額はHTLCなので、スクリプトを解くまでは特定の誰かに送金されるわけではない。

HTLCのスクリプトを解く条件は、基本的に以下のどれかになる。

  • 請求書のIDを入手する
  • タイムアウトする
  • 相手が裏切る

正常な送金であれば、送金先が請求書のIDを持っているので、それを使ってスクリプトを解き、自分の額に組み入れる。
もし相手が不在になったりして請求書のIDがわからないままになったとしても、時間制限があるため、それを過ぎると送金元が取り戻すことができるようになる。
相手が裏切って、古い配分のcommitment transactionを使った場合は、古い鍵情報を生成するためのper_commitment_secretを持っているので、裏切った場合の特別ルールで、相手の配分を含めてすべてを総取りできる。

以降では、正常な送金について解説していく。

[LN#006]Establish Channel (後半)

前回に引き続き、Establish Channelの説明を行う。
今回は後半の処理である。

前半の最後で、funderがfunding transactionの展開を行ったので、それが安定するまで待つ。
どのくらい待つかは、fundee側のaccept_channel.minimum_depthによって決められる。
minimum_depth以上のブロック数が経過すると、双方がfunding_locked メッセージを送信する。

メッセージではnext_per_commitment_pointを交換するが、これは次の鍵を生成するためのper_commitment_pointになる。
per_commitment_pointは鍵を生成するための重要な情報で、これ以降、commitment transactionの配分を変更するたびにBOLTメッセージを交換するが、配分を変更したことを確定するために、そこまでに使っていたper_commitment_pointの秘密鍵(per_commitment_secret)を相手に渡す。

per_commitment_secretと他のbasepoint情報があれば、相手が違反して古い配分のcommitment transactionをブロックチェーンに公開したとしても、そのトランザクションの出力をすべて手に入れることができる。
このしくみについては別の機会に説明するが、ここではper_commitment_point と per_commitment_secretの重要性だけ覚えておいていただきたい。

funding_lockedメッセージを交換すると、チャネルが開設したことになる。
状態名としては「Normal Operation」となる。
Normal Operationになると、相手のノードに対して送金することができるようになる。

ここまででチャネルは開設したが、チャネルの情報を他に転送できるかどうかは別の問題である。
チャネル情報を展開するのは、BOLT#7に記載されているannouncementという動作になり、チャネル開設とは別の次元で動いている。
展開するためにまた別のデータに署名するannouncement_signaturesというメッセージがあるが、funding transactionの経過ブロック数として最低でも6ブロックを必要としている。
そのため、accept_channel.minimum_depthが6より小さい場合は、Normal Operation状態になれたとしても、まだチャネル情報を他に展開することができず、よって送金をしてもらうことができない。

 

最後に、少し図が大きいがEstablish Channelの全体シーケンスを以下に示す。

[LN#005]Establish Channel (前半)

相手ノードとのネゴシエーションが終わり、initメッセージの交換が終わると、BOLTメッセージが交換できるようになる。

通常は、ノード間に開設したチャネルを使って送受金を行うため、開設するための「Channel Establish」について説明していく。

まず、ノードのどちらかが出資者(funder)になり、このチャネルでやりとりする総額を決定する。現在のBOLT仕様では、送受金はこの額の範囲内でしか行えない。

送金する場合は送金額を自分に割り当てられた額から減らして相手の割当額に加えるし、着金の場合はその逆になる。
それを繰り返し、チャネルを閉鎖したくなったときに、最後に残った配分をブロックチェーンに展開する。

 


Channel Establishは大きく2つに分かれる(BOLTでは前半・後半の呼び名はなく、ここでの説明のために分けている)。

  • funding transaction展開前(前半)
  • funding transaction展開後(後半)

funding transaction“というのは、上の図でいう橙色に当たる。
funderが出資したトランザクションで、その出力先はAさんとBさんのMultiSigになっている。
チャネルの双方が出資し合うように思ってしまうが、BOLT仕様では片方しか出資しない。

このトランザクションをブロックチェーンに展開し、相手が指定したブロック時間だけ経過すると、funding transactionとして有効と見なすことになっている。
今回は、前半だけ解説する。


まず、funder(シーケンス図の左)が open_channel メッセージを送信する。
メッセージに問題が無ければ、fundee(シーケンス図の右)が accept_channel メッセージを送信する。

どちらもパラメータが多いが、ここで出資するための情報や条件がほぼ表されている。
主なパラメータを以下に示す(主に、と書いたが、無視できるパラメータは無い)。

  • MultiSigのための公開鍵 : funding_pubkey
  • 前章に出てきた、鍵生成のためのパラメータ : xxx_basepoint
  • 出資額 : open_channel.funding_satoshis
  • 出資額のうち、相手に渡す額 : open_channel.push_msat
  • funding transaction展開後、トランザクションが安定したと見なす最短ブロック時間 : accept_channel.minimum_depth

Bitcoinの額は、最小単位がsatoshiである。
しかし、BOLTでの最小単位はmilli-satoshi(msat)で、1000分の1 satoshiである。
ここでは、push_msatがmsatになる。

push_msatは、単に配分として相手に渡すという仕様があるだけで、それを渡すための条件はBOLTで決められていない。
もしpush_msatに値がある状態でチャネルを開設し、そのまま取引をせずにチャネルを閉鎖しても、相手にはpush_msatをsatoshiに変換した額が渡されてしまうので、扱いについては注意が必要である。

 

accept_channelをチェックして問題が無ければ、 funding_createdメッセージを送信する。
この時点で、チャネルを閉鎖した際、funderとfundeeにいくらずつ配分すべきかのトランザクションを決定することができる。

これはfunding transactionの出力となるトランザクションで、BOLTでは “commitment transaction”と呼ぶ。
以降、commitment transactionでfunderとfundeeの配分を変化させていくことで送受金処理を行っていく。

 

BOLTではトランザクションの構成を細かく決めることで、トランザクション自体の情報を交換せず、ブロックチェーンに展開する際の署名だけを渡すことが多い(funding transactionが2-of-2 MultiSigのため、その出力であるcommitment transactionを展開するためにはお互いが署名する必要がある)。

ブロックチェーンに見える部分だけの署名関係を簡易図にすると、以下のようになる(txはトランザクションの略)。
commitment transaction(commit_tx)の送金先は、実際はもう少し複雑であるが、基本的にはこうなる。

 

funding_createdメッセージでは、funderが初回のcommitment transactionに署名して、相手に渡す。
また、込みいっていることに、commitment transactionの構成はAさんとBさんで異なる。

これは、funderとfundeeで異なる訳ではなく、トランザクションの項目に”local”と”remote”があり、自分が作るトランザクションの場合は「local=自分」「remote=相手」になり、相手が作るトランザクションの場合は「local=相手」「remote=自分」になるためである。

トランザクションの展開には手数料(fee)も必要になるが、手数料はトランザクションを展開する人の額から差し引くことになっているため、額についてもどちらが展開するかによって変わってくることになる。

 

以上を踏まえて図を修正すると、以下のようになる。

そして、funding_createdで送信する署名は、相手が展開するcommitment transactionに対する署名である。

よって、commitment transactionは自分用と相手用の両方を作り、メッセージで送信する場合は相手のcommitment transactionに対しての署名を送信し、メッセージで受信した署名は自分のcommitment transactionでチェックすることになる。

funding_signedを受信して問題が無かった場合、funding_signedメッセージを送信する。
このメッセージも署名を含んでいるが、funding_createdと同様に「相手の持つcommitment transaction」に行った署名になる。

ここまでメッセージを交換すると、funding transactionを展開してから相手が不在になったとしても、自分に取り戻すcommitment transactionを展開することができる状況になったので、funding transactionをブロックチェーンに公開する。

ここまでがEstablish Channelの前半である。

[LN#004]鍵

これからノード間で送金を行うための準備を行う。

送金するためには「チャネル」と呼ぶ関係を作る。
チャネルができた後は、チャネルについた識別子(channel_id)をBOLTメッセージに載せて通信をしていく。
チャネルを開設する処理を「Channel Establishment」と呼んでいる。

https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md#channel-establishment

Channel Establishのシーケンスを以下に示す。

 

Channel Establishにて、on-chainからoff-chainに制御を移す。
移す際に、FunderとFundeeのMultiSig(2-of-2)に送金を行う。

その2-of-2の鍵や、不正を行わないようBitcoinのトランザクションにスクリプトを作るための鍵などがあり、それらをEstablish中に交換する。
鍵を直接交換するのでは無く、鍵を生成するための情報を交換し、各自で計算することになる。
計算については、以下を参照のこと。
https://github.com/lightningnetwork/lightning-rfc/blob/master/03-transactions.md#keys

また、鍵は固定ではなく、2-of-2トランザクションの出力内容を更新するたびに新しく作り替え、古い鍵情報は廃棄(revoke)する。
送金を行った後で古い出力内容をon-chainに載せられてしまうと、Bitcoinとしてはon-chainに見える送金情報しか確認できないため、結果として行った送金が無かったことになってしまうためである。
ただ、廃棄したことを約束事だけにしてしまわず、トランザクションのスクリプトと、BOLTメッセージのルールを組み合わせることで、古い出力内容をon-chainに展開すると、展開した方が損をする、というしくみになっている。

鍵(公開鍵)は、以下の種類がある(2017/11/27現在)。

  • localkey
  • remotekey
  • local_delayedkey
  • remote_delayedkey
  • local_revocationkey
  • remote_revocationkey
  • local_htlckey
  • remote_htlckey

localとremoteがあり、お互いが作成できるだけの情報を交換する。
これらの鍵を使って、それぞれが2-of-2トランザクションからの出力(commitment transaction)を作成できるようになっている。

このように、鍵については慎重に考えられているが、ここでは、以下を認識しておけば良いだろう。

  • 鍵を直接交換しないで交換した情報から生成する
  • 最初の情報交換は、Channel Establish時に行う
  • 2-of-2トランザクションからの出力内容(commitment transaction)が変わるたびに、鍵を作り直す
  • 古い鍵を使ったcommitment transactionを使うと損する設計になっている
参照

https://github.com/nayutaco/ptarmigan/blob/development/ucoin/src/ln/ln_derkey.c

[LN#003]init

前回、ノード間のネゴシエーションを行った。

ネゴシエーション後、最初に送信するBOLTメッセージは”init”である。

https://github.com/lightningnetwork/lightning-rfc/blob/master/01-messaging.md#the-init-message

 

描画ツールの都合上、initがシーケンスのようにやりとりしているようになってしまったが、実際はどちらが先に送信しても良い。

ただし、initを受信するまでは次のBOLTメッセージを送信することはできない。

initメッセージはglobalとlocalのfeatureをパラメータとして持つ。
featureの内容は、BOLT#9に記載されている。
https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md

featureのデータは最短長になるように送信しなくてはならない。
例えば、現時点ではgolbalfeaturesの定義が無いので「0x00」を送信したくなるが、立てるビットがない場合には送信する必要がないため、ここではgflenを0にして、パラメータ自体を送信しないようにする。
あるいは、奇数ビットについては受信側で無視して良いことになっているため、そういうデータなら送信してもよい。

チャネル接続後、ネットワークが切断されて再接続する場合は、initメッセージのあとに”channel_reestablish”を送信する。
再接続については、説明を省略する。
https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md#message-retransmission

参照

https://github.com/nayutaco/ptarmigan/blob/development/ucoin/src/ln/ln_msg_setupctl.c

[LN#002] Noise Protocol

BOLTは、ノード間でメッセージ交換する。

まず、相手ノードとのネゴシエーションを行い、成功したらその相手とBOLTのメッセージを符号化して通信する。
BOLT#8では、ネゴシエーションの方法と、その後のメッセージ符号化/復号化について説明している。
このBOLT#8だけは、他の章からほぼ独立している。


https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md

ネゴシエーションや符号化の仕方は、Noise Protocolという方式を用いている。
Noise Protocol自体はいくつかパラメータがあり、そのうちBOLTで使用するものだけ#8に記載されている。
シーケンスを以下の図に示す。

 

ここでは、AがBに対して接続を要求するようになっている。
相手ノードとはIPアドレスなどでTCP接続を行った後、相手をnode_id(Bitcoinの公開鍵と同じ計算で求めた33バイトのデータ)で指定してネゴシエーションを行う。
1.5往復して相手のノードに間違いが無ければ、ネゴシエーションが完了する。


それ以降は、ネゴシエーション時に交換した鍵情報で、BOLTのメッセージを符号化/復号化を行う。
メッセージ構造は、以下に記載されている。

https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#lightning-message-specification

 

大きく「メッセージ長」と「メッセージ本体」に分かれる。
メッセージ長はChaChaPoly-1305で符号化+MAC付加する(2+16byte)。
メッセージ本体も同様にChaChaPoly-1305で符号化+MAC付加する(メッセージ長+16byte)。
その両者を結合して、1つのメッセージとして相手に送信する。
受信はその逆の動作を行って復号化する。

 

符号化/復号化の鍵はネゴシエーション時のものを使い続けるのではなく、1000回符号化/復号化するたびにrotation処理を行い、鍵を変更する。
1回の送信で2回の符号化を行うため、500回のメッセージ送信でrotation処理を行うことになる。
https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#lightning-message-key-rotation

 

参照

https://github.com/nayutaco/ptarmigan/blob/development/ucoin/src/ln/ln_enc_auth.c

[LN#001] どこから読むか

現在(2017/11/14)のところ、BOLTは0~11章まである(うち、6章は欠番)。

 

このうち、どこから読み始めるのがよいかという質問をされることがあるが、その回答は難しい。


まず、BOLT#0は導入部で用語が多少載っているため、先に目を通すとよいだろう。
テーマソングがあるなど、ふざけたところがあるように見えるかもしれないが、そういった要素はここ以外見当たらない。

 

内容についてはBOLT#1以降になるが、プロトコルの階層順で行くと、BOLT#8を最初に読むのがよいだろう。
BOLT#8は他の章からほぼ独立していて、BOLTでの通信を開始する前のネゴシエーションと、通信を開始してからの符号化処理について書かれている。
他の章は、この章の内容が実現されている前提になっている。
逆に言えば、実装をしないのであれば、BOLT#8はあまり気にしなくてよい。

 

BOLT#1、#2および#7は、P2P通信プロトコルについて記載されている。
BOLT#1,#7で使用するパラメータの一部をBOLT#9で、BOLT#2で使用するパラメータの一部をBOLT#4で詳細に説明している。
BOLT#3は、1st Layer、すなわちBitcoinのブロックチェーンで使用するトランザクションとBOLT#2で交換したメッセージの関係や、スクリプトで使用する鍵の導出について記載されている。
BOLT#5は、トランザクションをon-chain、すなわちBitcoinのブロックチェーンでどのように扱うべきか示している。

 

BOLT#10はノードの情報を得る手段について、BOLT#11は請求書(invoice)についてである。


このように、章単独で読み進められるものではない。
それでもあえてどこからか読み進めるとすれば、BOLT#2がよいか。
内容は分からないにしても、シーケンスが図になっているので、イメージはつかみやすいと思う。

 

ただ、このブログでは、通信を確立するところから順に解説していく。


なお、弊社ではオリジナルノード実装のptarmiganを開発している。
BOLTの標準実装を進めているc-lightningのノードと混在させての送受金テストも動作している(commit id : ebdecebb1a89f7dcd8daa53c57ec58af32f7c40d)。