[LN#013]現状(2017年12月前半)

ここまででChannel Establishment、Normal Operation、Channel Closeの主要な説明を行ってきた。

今回は、現状(2017/12前半)のBOLTについてまとめておく。
BOLTのコミットはこちらである。


2017/12/7に、以下の記事が掲載された。
Lightning Protocol 1.0: Compatibility Achieved ✅ – Lightning Developers – Medium

これを掲載した時点(2017/12/20)で、またBOLTとしてv1.0はリリースされていない。
記事で、c-lightningeclairlndの3ノードアプリ間で統合テストが通ったという内容であった。

基本的には、現在の内容からは大きく変更せずにv1.0として固めるのだろうと思われる。
issueもあるものの、現在はドキュメントとしての整備が行われているようで、最近のコミット内容は英語の文法や見栄えのような習性が多い。

 

v1.0には入れないが、次のv1.1に入れたいという内容は上がってきている。
https://github.com/lightningnetwork/lightning-rfc/milestone/1


現状のBOLT仕様ではいくつか数値の制限があるので、amountや期間に関係するものをいくつか挙げておく。

 

Channel Establishment

funding_satoshisは、チャネルにfundingする額である。
当初は、funding_satoshisなどの額については4byteで管理していたのだが、Litecoinではコーヒーも飲めないということで8byte管理になったようである。
なお、ブロックチェーンの種類はopen_channel/accept_channelにgenesis blockのハッシュを載せることで表している。

max_accepted_htlcsは、受入可能なHTLC数ということで、送金反映でHTLCが消える前に別の送金が行われるなどしてHTLCがたまっていった場合の上限と考えている。
この半端な数字は、違反した相手から取り戻すときのことを考慮しているようである。

minimum_depthは、相手がチャネル開設を要求してきた(open_channelを送信)とき、自分はfunding transactionを認めるのにconfirmationがこれだけ必要になる、という下限である。
表現として「大きすぎる(unreasonably large)」は曖昧だが、ノードを立てた人の感覚を重視しているのかもしれない。

 

Normal Operation

ホップ数は最大で20もあるため、update_add_htlcメッセージは1,366byteと固定のBOLTメッセージで一番大きくなっている。
また、onion_routing_packetは送金経路を隠蔽するようにできているため、ホップ数にかかわらず1,366byte送信する必要がある。

[LN#012]Channel Close

Channel Establishで開設したチャネルは、通常、Channel Closeで閉鎖する。
お互いが同意しているので、Mutual Closeと呼んでいる。

 

使用するBOLTメッセージは2種類で、shutdownとclosing_signedである。
前提条件は、HTLCが残っていない(no pending charges)ことである。

まず、どちらかがshutdownメッセージに送金してほしいアドレス(正確にはscriptPubKey)を伝え、それに応じて相手もshutdownメッセージを返す。
後は、チャネル閉鎖時にfunding transactionから送金するトランザクションであるclosing transactionの署名とそのときの手数料をclosing_signedメッセージで交換し合い、相手の提示する手数料に納得したら同じ手数料でclosing_signedを返して終わりとなる。
(この連載中、open_channel/accept_channelメッセージにshutdown_scriptpubkeyが追加されたが、オプションのため今回は除外する。)

以下は、最短で閉じた場合のシーケンスである。

image

 

注意したいのは、一度でも署名を送信していると、そのトランザクションに相手が署名を追加してブロックチェーンに展開することが可能な点である。
自分がclosing_signedを送信した=その手数料で送信しても問題ない、ということになるため、困ることはないと思われるが、最後に交換したclosing_signedで必ずしも展開されるわけではないということは覚えておいた方がよいだろう。

[LN#011]送金 (5)

前回はcommitment transactionの構成と、その送金先に指定するoutput(to_local, to_remote, Offered HTLC, Received HTLC)について説明した。
今回は、その送金先について解説する。


to_local outputは、通常は以下のようなunlocking scriptを使って使用する(通常ではない=古いcommitment transaction)。

image

ここのto_self_delayは、open_channel/accept_channelで相手が指定したto_self_delayを使用する。
この値はOP_CSV(CHECKSEQUENCEVERIFY)の判定に使われ、このトランザクションを展開してからto_self_delay以上のブロックが経過しないと展開できない。

このスクリプトはunlockingのためにlocal delayedsecretkeyで署名する必要があるため、commitment transactionを展開した人(=delayedsecretkeyを知っている人)が使えるようになっている。


to_remote outputは、P2WPKHへの送金になる。
スクリプトではないため、何もせずに使用できる。

これは、commitment transactionを展開していない人への送金になっている。


Offered HTLC outputsは、commitment transactionをどちらが展開したかによってunlocking scriptが変わる。

 

自分が展開

この場合は、2-of-2になっていて、署名として自分と相手の両方が必要になる。
相手の署名は、commitment_signedのhtlc_signatureで受け取る。

image

自分が展開したOffered HTLC outputは、自分が相手に送金したHTLCになるため、通常は「相手が受け取らずにタイムアウトしたときに取り戻す」ときに解くことになる。

相手に署名してもらうためには、トランザクションが確定している必要がある。
そのトランザクションはHTLC-Timeout Transactionという名前になっている。

image

 

相手が展開

相手が展開したcommitment transactionに載っているOffered HTLC outputsは、自分が受け取る予定のため、payment_preimageを必要とする。

image

これは自分だけで処理できるため、解いた後の送金先を指定できる。

image


Received HTLC outputsも同様に、どちらがcommitment transactionを展開したかによって変わってくる。

 

自分が展開

この場合は2-of-2になっていて、署名はOffered HTLC outputsと同じくcommitment_signedメッセージで受け取っている。
また、自分が受け取る予定のため、payment_preimageも必要である。

image

相手に署名してもらうためには、トランザクションが確定している必要がある。
そのトランザクションはHTLC-Success Transactionという名前になっていて、構成はHTLC-Timeout Transactionとほぼ同じである。

image

 

相手が展開

相手が展開したReceived HTLC outputsは、自分の送金に対応している。
それを使用できるようになるのはタイムアウトした場合である。

image

こちらについては、送金先を自由に決めることができる。

image


commitment transactionをブロックチェーンに展開した後の送金先について説明した。
次回は、BOLTメッセージを使ってお互いが同意してチャネルを閉鎖する場合について解説する。

[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の全体シーケンスを以下に示す。

image