[LN#020]HTLC (1)

Lightning Network BOLTの概要について、書いていくシリーズ。

今回は、BOLTで使われているHTLCについて説明します。
以前「[LN#010]送金(4)」でも説明しましたが、もう少しゆっくりやります。


HTLCは、Hashed TimeLock Contractsの略で、BIP112などに説明があります。

BitcoinにはEthereumのようなスマートコントラクトみたいなものは今のところないのですが、IF文は使うことができます。
また、トランザクションのバージョン2からは、OP_CHECKLOCKTIMEVERIFYとOP_CHECKSEQUENCEVERIFYという命令も使うことができます。
(使用できる命令については、wiki/Script参照。)

 

例えば、こちらはregtestで作成したcommitment transactionです。
voutに「to_remote」「Received」「to_local」と書かれていますが、「Received」と「to_local」についてはスクリプト(P2WSH)への送金になっています。

image

 

BOLTでのHTLCは「Offered HTLC」「Received HTLC」になりますが、今回はBitcoinでのスクリプトを中心に説明するため、to_localについて見ていきます。


ここでのto_localスクリプトは、このような構造になっています。

image

 

この構成は、BOLT#3で決められています。

image

 

OP_IF~OP_ELSE~OP_ENDIFという、比較的見慣れた構造になっているのが分かるかと思います。
Bitcoinのスクリプトは、FORTHに似ているといわれています(私はよく知りません)が、私は逆ポーランド記法のようなイメージで考えています。

値(1や0など)をスタックし、次にOP_IFに来ると、直前に積まれているスタックの内容を判断して、OP_IFの方かOP_ELSEの方かの分岐が生じるようになっています。
0が偽で、それ以外は真という扱いです。

最後のOP_CHECKSIGは、スタックから2つ取り出して、1つは署名、1つは公開鍵として署名のverifyを行い、真か偽かをチェックするという命令です。


簡易表記していますが、OP_IFのルートを通す場合は、このようなスタックの動きになります。
左から右に向かって読んでください。
太線の下が動作で、例えば一番左側であれば「”rev-sig”という値をスタックに積んだ」ですし、左から3番目であれば「OP_IFのルートを通る」という意味です。

image

 

OP_IFのルートでは<revocationpubkey>という公開鍵をスタックすることになっているので、その前に<revocationpubkey>の秘密鍵で署名したデータをスタックしておかなくてはなりません(ここでは “rev-sig”と記載)。

そして、OP_IFのルートを通すために”1”をスタックし、あとはスクリプトを順番に処理していきます。


では、OP_ELSEルートを通してみましょう。

この場合は、<local_delayedpubkey>がスタックされるので、前と同様に<local_delayedpubkey>の秘密鍵で署名したデータをスタックしておかなくてはなりません。

しかし、その前にOP_CSV(OP_CHECKSEQUENCEVERIFY)があります。
これは、直前にスタックされている値を取り出し、vinとなるトランザクションがブロックに入ったときのブロック高+スタック値以上のブロック数が経過していない場合はスクリプト自体が偽となる命令になっています。

 

スタックの動きは、こうなります。
OP_DROPは、OP_CSVの結果が真だった場合、スタックしていたto_self_delay値が邪魔になるので、取り除くのに使われています。

 

image

 

そうなると、OP_CSVを経由しない方がすぐに使用できるので、そちらを使いたくなるかもしれません。
しかし、<revocationpubkey>の秘密鍵を使うことになるのは、revoked transaction closeをされた場合、すなわち相手が古いcommitment transactionをブロックチェーンに展開した場合に、ペナルティーとして相手のすべてを取り戻す場合になります。

ブロックチェーンに展開されているので、to_self_delayブロック経過してしまうと、相手がOP_ELSEルートを通るスクリプトを作ることができてしまうので、それまでに取り戻さないといけません。
to_self_delayはChannel Establish時にopen_channel / accept_channelで交換するので、小さすぎず(ペナルティーとして有効になるくらいの期間)、大きすぎず(unilateral closeで自分に取り戻すのに長すぎない期間)であることを確認する必要があります。


今回は、主にto_local scriptの解き方について説明しました。

BOLTでHTLCというと、送金/着金が反映される前、最初のトランザクションでいえば「Received」のようなものを指しますので、次回はそれらについて説明する予定です。