HTLC完全に理解した

ブロックチェーン・プログラミングという本を読んだ。
この本にはLightning Networkに使われているHTLCに関する記載がある。
そこを理解するのに時間が掛かったので、忘れないうちにメモを残しておく。

非技術者向けの話

マイクロペイメントチャンネル

Bitcoinの送金には手数料と時間が掛かる。
だが、無料で送金したいし、一瞬で送金されてほしい。
それを実現するための方法の一つがマイクロペイメントチャンネル。
(スケーラビリティの話はこの記事では無視する。)

方法

ざっくりした話をする。
AliceからBobへの送金を考える。
この時、AliceとBobの共有口座を作っておく。
例: Aliceが1BTC、Bobが1BTCを出資して、2BTCが入った共有口座を作る。
その後で、共有口座からの引き出し権を設定する。
例: 2BTCが入った共有口座からの引き出し権を、Aliceが0.5BTC分、Bobが1.5BTC分持っていることとする。
この状況を観察すると、Aliceが持っていた1BTCが0.5BTCの引き出し権になり(-0.5)、Bobが持っていた1BTCが1.5BTCの引き出し権になっている(+0.5)。
「共有口座からの引き出し権の設定」によって、実質的にAliceからBobに0.5BTCの送金が行われたとみなせる。

最初に言った「無料で送金したいし、一瞬で送金されてほしい」を実現するためには、「共有口座からの引き出し権」の管理を、第三者へのトラストを使わず、かつブロックチェーンを使わずに行う必要がある。
(第三者へのトラストを使うのであれば、そもそもBitcoinを使う必要がない)
(ブロックチェーンを使えば、手数料と時間が掛かってしまう)
その方法は、技術者向けの話の章に書く。

Lightning Network

上記のマイクロペイメントチャンネルの説明で、一つあえて触れなかったことがある。

例: Aliceが1BTC、Bobが1BTCを出資して、2BTCが入った共有口座を作る。

の部分で、共有口座に、AliceとBobがそれぞれブロックチェーンを使って送金している。
結局、手数料と時間が掛かってしまう。
マイクロペイメントチャンネルを使う意味が無いように思えるかもしれない。

例えば、AliceからBobに、通常の方法を使って5回送金する場合を考える。

5回分の手数料と時間が掛かる。

対して、AliceからBobに、マイクロペイメントチャンネルを使って5回送金する場合を考える。

2回分の手数料と時間で5回の(実質的な)送金を行うことができた。
お得。

つまり、送金にマイクロペイメントチャンネルを使う意味があるかどうかは、同じ相手に何回送金する予定があるかによって変わってくる。
だが、そんな予定が立つことはめったに無い。

まとめると、マイクロペイメントチャンネルは使いづらい。
それを解決してくれる技術として、Lightning Networkがある。

方法

ざっくりとした話をする。
先の例と同じように、AliceがBobにマイクロペイメントチャンネルを使って0.5BTCを送金するとする。
以下のような流れになる:

ここまでは先の例と同じだが、追加で以下の状況を仮定する:

この状況下で、AliceがCarolに0.1BTCを送金したくなったとする。
この時、AliceとCarolの間に共有口座は無い。
しかし、AliceからCarolへ、マイクロペイメントチャンネルを使って送金することが可能。
以下のような流れになる:

この状況を観察すると、

となり、AliceからCarolへの送金が(実質的に)成功している。
そして、この(実質的な)送金に、ブロックチェーンは使われていない。
そのため、手数料も時間も掛からない。

今回の例で、Aliceは、Bobとの間の共有口座の引き出し権を更新することで、BobとCarolへ手数料も時間も掛けずに送金できることが分かった。

そしてこの話は一般化できる。
もしCarolがDaveとの間に共有口座を持っていれば、AliceはBobとCarolとDaveへ手数料も時間も掛けずに送金できる。

BobがさらにEllenとの間に共有口座を持っていて、EllenがFrankとの間、Georgeとの間にそれぞれ共有口座を持っているとすれば、AliceはBobとCarolとDaveとEllenとFrankとGeorgeへ手数料も時間も掛けずに送金できる。

結論を言うと、共有口座でネットワークを構成すれば、一度誰かと共有口座を開くだけで誰に対しても手数料も時間も掛けずに送金できる状況を作り出せる。

(現実にはそう簡単な話ではないのだが、端折る)

これがLightning Network。

HTLC

上記のLightning Networkの説明で、一つあえて触れなかったことがある。

Alice「ねぇBob、あなた、Carolとの間に共有口座を持っているでしょう。その共有口座の話なんだけど、Carolに追加で0.1BTCの引き出し権を与えてくれない?代わりに、私とあなたの間の共有口座から追加で0.1BTCを引き出させてあげるわ」

Aliceは、Bobに対して、「お願い」をしているだけ。
Bobは、Aliceから0.1BTCの引き出し権を受け取った後、Carolへ0.1BTCの引き出し権を与えることをサボることができる。
その結果、Bobは、本来Carolに渡るはずだったAliceの0.1BTCを盗むことができる。
これを防ぐための技術がHTLC。
詳細は、技術者向けの話の章に書く。

技術者向けの話

マイクロペイメントチャンネル

非技術者向けのマイクロペイメントチャンネルの話の章に、

「共有口座からの引き出し権」の管理を、第三者へのトラストを使わず、かつブロックチェーンを使わずに行う必要がある。
その方法は、技術者向けの話の章に書く。

と書いたので、まずはここから書く。

マイクロペイメントチャンネルを使った送金の流れをおさらいすると、以下のようになる。

これまで「共有口座」という例えを使ってきたが、これをBitcoin語で、2of2マルチシグアドレスと言う。
AliceとBobの2of2マルチシグアドレスの場合、AliceとBobの両方の署名が無ければ、そのアドレスからBTCを引き出すことは出来ない。
「AliceとBobがそれぞれ1BTCを出資して共有口座を作る」をBitcoin語に訳すと、「AliceとBobの2of2マルチシグアドレスに、AliceとBobがそれぞれ1BTCずつ送金する」となる。
以下で共有口座と言った場合には、AliceとBobの2of2マルチシグアドレスを指すものとする。

次は「引き出し権の更新」の実態を見ていく。
「引き出し権」は、Bitcoinの世界では、共有口座からAliceとBobへBTCを移すトランザクションとして表現される。
例えば、(Alice: 1BTC, Bob: 1BTC)の引き出し権は以下のようなトランザクションで表現される。

このようなトランザクションを、commitment txと呼ぶ。
「引き出し権の更新」は、以下の流れで行う。

そうすることで、AliceはBobから送られてきたBobの署名と自分で生成したAliceの署名を組み合わせて2of2マルチシグアドレスをアンロックできる。
Bobもまた、Aliceから送られてきたAliceの署名と自分で生成したBobの署名を組み合わせることで、2of2マルチシグアドレスをアンロックできる。
AliceとBobは、その気になった時にcommitment txをブロードキャストすることで、BTCを引き出せる。

ここまでの話には不足している部分がある。
一度しか引き出し権を設定しないならこれでも良いのだが、実際には引き出し権は何度も更新される。
引き出し権の更新は、新しいcommitment txをAliceとBobの間で交換することで行う。
その際、更新前の古いcommitment txを使用不能にする仕組みが必要となる。
古い引き出し権を行使されてしまうならば、引き出し権を「更新」しているとは言えないからだ。
ハッシュを上手く使い、commitment txに細工をすることで、その仕組みを実現できる。

本当の「引き出し権の更新」は、以下の流れで行う。

(Alice: 0.5BTC, Bob: 1.5BTC)の引き出し権を表現する、Aliceが作り、Aliceが署名し、Bobに渡すcommitment txは、以下のような物になる。

反対側も一応書くと、(Alice: 0.5BTC, Bob: 1.5BTC)の引き出し権を表現する、Bobが作り、Bobが署名し、Aliceに渡すcommitment txは、以下のような物になる。

Aliceが作るcommitment txを例にとって、意味を見ていく。
まずは、「このcommitment txを作る際にBobから送られてきたハッシュの原像があれば、Aliceの署名でアンロックできるようになる」の部分に注目する。
前述したように、新しいcommitment txを作る際には、古いcommitment txを作る際に使ったハッシュの原像がバラされる。
なので、「このcommitment txを作る際にBobから送られてきたハッシュの原像があれば」は、「新しいcommitment txが作られた後にこのcommitment txがブロードキャストされたら」という意味になる。
全体を書くと、「新しいcommitment txが作られた後にこのcommitment txがブロードキャストされたら、Aliceの署名でアンロックできるようになる」。
そして、

の3つを考慮すると、「Bobが古いcommitment txをブロードキャストしたことにAliceがすぐに気付いた場合、Aliceは共有口座の中身を全額手に入れられる」ということが導ける。
Bobに、古いcommitment txを使うことに対する経済的非合理性を与えることに成功している。
これが「更新前の古いcommitment txを使用不能にする仕組み」だ。

「共有口座からの引き出し権」の管理を、第三者へのトラストを使わず、かつブロックチェーンを使わずに行う必要がある。

これを実現できた。

Lightning Network / HTLC

Alice, Bob間の共有口座とBob, Carol間の共有口座を使ってAliceからCarolへ0.1BTCを(実質的に)送金する場合、Bobがその0.1BTCを持ち逃げできるという話があった。
これを防ぐ方法について見ていく。

これまで、

という順番で考えてきたが、これを逆転させる。

Bobは一度赤字を掘ってから、Aliceからの送金でそれを補填する。

そうすると、やるべきことは、持ち逃げを防ぐことから、「Bob -> Carolの送金が行われた場合にAlice -> Bobの送金が行われることの保証」に変化する。
これは、BobからCarolへの送金が行われた場合にのみブロードキャストできるAliceからBobへの送金トランザクションを、Aliceが事前に作っておき、Bobに渡しておくことで実現できる。
以下で、「BobからCarolへの送金が行われた場合にのみブロードキャストできるAliceからBobへの送金トランザクション」の作り方を見ていく。

技術者向けのマイクロペイメントチャンネルの話の章に書いた方法で、Alice <-> Bob、Bob <-> Carolの共有口座が開かれ、最初のcommitment txが作られ、交換されているものとする。
それぞれの共有口座の引き出し権は、(Alice: 1BTC, Bob: 1BTC) (Bob: 0.5BTC, Carol: 1.5BTC) となっているものとする。
AliceからCarolへ、0.1BTCを(実質的に)送金したいとする。
以下の流れで行う。

長い。
まずは、Alice <-> Bob間を見ていく。
Aliceが作り、Aliceが署名し、Bobに渡すcommitment txの形は、以下のようになる。

それぞれ、0.9BTCがAliceの引き出し権、1BTCがBobの引き出し権、0.1BTCがCarolの引き出し権(をBobに増やしてもらうためのBobの引き出し権)である。
0.9BTCと1BTCのアンロック条件の意味はマイクロペイメントチャンネルの時と同じなので、0.1BTCのアンロック条件を見ていく。

0.1BTCの2つ目のアンロック条件と1BTCの2つ目のアンロック条件は、同じもの。
意味も、マイクロペイメントチャンネルの時と同様、「新しいcommitment txが作られた後にこのcommitment txがブロードキャストされたら、Aliceの署名でアンロックできるようになる」で変わりない。

0.1BTCの1つ目のアンロック条件は、1BTCの1つ目のアンロック条件とほぼ同じだが、「Rの原像があれば」という但し書きが付いている。
詳細は後述するが、Rの原像は、BobからCarolへの送金がブロードキャストされた場合にバレる。
つまり、「Rの原像があれば」は「BobからCarolへの送金が行われた場合」と解釈できる。
前述した2つ目のアンロック条件も考慮して全体を読むと、「BobからCarolへの送金が行われ、かつこれが最新のcommitment txである場合、Bobの署名でアンロックできる」と解釈できる。

3つ目のアンロック条件を見ていく。
そもそもこのアウトプットは、BobがCarolへ(実質的に)送金した場合の赤字を補填するためのもの。
つまり、BobがCarolへ(実質的に)送金しなかった場合、このアウトプットはAliceの元へ帰る必要がある。
そのためのアンロック条件。

全てのアウトプットを総合して考えると、Bobがこのcommitment txをブロードキャストすることが合理的なのは、BobからCarolへの送金が行われ、かつこれが最新のcommitment txである場合のみ。

次に、Bobが作り、Bobが署名し、Aliceに渡すcommitment txの形を見ていく。
以下のようになる。

例によって最初の2つのアウトプットはマイクロペイメントチャンネルの時と同じ意味なので、0.1BTCのアウトプットを見ていく。

0.1BTCのアウトプットの2つ目のアンロック条件は、これまた例によって1BTCの2つ目のアンロック条件と同じ意味。
「新しいcommitment txが作られた後にこのcommitment txがブロードキャストされたら、Bobの署名でアンロックできるようになる」

1つ目のアンロック条件は、これまた例によって「BobからCarolへの送金が行われ、かつこれが最新のcommitment txである場合、Bobの署名でアンロックできる」の意。

3つ目は、これまた例によって、BobがCarolへ(実質的に)送金しなかった場合のAliceへの返金用。
「ブロードキャストから一定期間経過した場合」は2つ目のアンロック条件をBobに使わせるための猶予。
「ブロック高が(このcommitment tx作成時のブロック高 + 2000)以上になり」は、Bob <-> Carol間の共有口座からの引き出しが、Alice <-> Bob間の共有口座からの引き出しより早く起こることを保証するために使う。
詳細は後述。

全てのアウトプットを総合して考えると、Aliceがこのcommitment txをブロードキャストすることが合理的なのは、BobからCarolへの送金が行われず、かつこれが最新のcommitment txである場合のみ。

2つの対になったcommitment txを見てみると、BobがCarolへ0.1BTCを送金した場合、BobはAliceとの共有口座から追加で0.1BTCを引き出せ、BobがCarolへ0.1BTCを送金しなかった場合は、引き出し権に変化が起こらないようになっていることが分かる。
前述した「Bob -> Carolの送金が行われた場合にAlice -> Bobの送金が行われることの保証」が出来ている。

次に、Bob <-> Carol間を見ていく。
Bobが作り、Bobが署名し、Carolに渡すcommitment txの形は、以下のようになる。

基本的にAliceが作ってBobに渡していたcommitment txと同じ。
0.4BTCがBobの引き出し権、1.5BTCがCarolの引き出し権、0.1BTCが(Aliceから渡ってきた)Carolの引き出し権。

前に、「Rの原像があれば」は「BobからCarolへの送金が行われた場合」と解釈できると書いた。
これは厳密には、commitment txのアウトプットを、CarolがRの原像を使って消費した場合という意味。
「Rの原像があれば」の条件が付いたアウトプットを消費しようとすると、それを消費するトランザクションのscriptSigにRの原像を含め、それをブロックチェーンに記載することになるので、AliceにもBobにもバレてしまうという話。

Carolが死んでいる場合、commitment txがいつまで経ってもブロードキャストされないことや、ブロードキャストされてもそのアウトプットが消費されないことがある。
その場合、Bobは3つ目のアウトプットを使って、赤字を取り戻すことができる。

Aliceが作ってBobに渡していたcommitment txで「ブロック高が(このcommitment tx作成時のブロック高 + 2000)以上になった場合」となっていたところが「ブロック高が(このcommitment tx作成時のブロック高 + 1000)以上になった場合」となっている。
もしもこの差が無かったとすると、AliceからBobに(実質的に)送金される予定だった0.1BTCがAliceの元に返った後に、Carolがcommitment txのアウトプットを消費するという流れが発生しうる。
その場合、Aliceの資金は±0, Bobの資金は-0.1、Carolの資金は+0.1、という結果になり、Bobが赤字を出してしまう。
これを防ぐために、ブロック高の差を利用して、Bob <-> Carol間の共有口座からの引き出しが、Alice <-> Bob間の共有口座からの引き出しより早く起こることを保証する必要がある。

一応、Carolが作り、Carolが署名し、Bobに渡すcommitment txの形を書いておく。

Bobが作り、Bobが署名し、Aliceに渡すcommitment txと同じなので、特に書くことがない。

感想



ブロックチェーン・プログラミングには、Alice <-> Bob, Bob <-> Carolのマイクロペイメントチャンネルを閉じずにAlice -> Carolの送金を終わらせる方法とかも書いてあったので、気になる方は買うと良いと思います。

何か指摘があれば@tock203まで。