エンジニアの光田(@34ro)です。
以前投稿した「Payment Channelを時系列で理解する」の続きで、Payment Channelで使われているスクリプトを追ってみようと思います。今回はBOLTと呼ばれるLightning Networkのプロトコルで規定されているスクリプトを題材にしました。
前提となるビットコインのスクリプトについては「ビットコインとブロックチェーン(PDF版)」の「第5章 トランザクションscriptとScript言語」で解説されています。
P2WPKHやP2WSHについては「ビットコインはsegwitで何が変わるのか?技術な要点まとめ」でも少し説明しています。
前回の要点とHTLC,BOLTについて
「Payment Channelを時系列で理解する」では、AliceとBobはチャンネルをアップデートするたびに新しい鍵とそのハッシュを生成し、新しいハッシュと、前回の鍵を明かしました。
そうすることで
・Bobのシステムがダウンしても、Aliceは手元のトランザクションをブロードキャストし、タイムアウトまで待てば資産を取り戻せる
・Aliceが持っている過去のトランザクションをブロードキャストしたとしても、Bobに奪われてしまう
また
・Aliceのシステムがダウンしても、Bobは手元のトランザクションをブロードキャストし、タイムアウトまで待てば資産を取り戻せる
・Bobが持っている過去のトランザクションをブロードキャストしたとしても、Aliceに奪われてしまう
という状況を作り出しました。これにより過去の状態(例: Alice: 5BTC, Bob: 5BTC)を無効化(裏切って過去の状態をブロードキャストしても資産を奪われてしまう)しつつ、新しい状態(例: Alice: 6BTC, Bob: 4BTC)に遷移することでオフチェーンの送金を実現していました。
前回のブログのようにハッシュとタイムアウトを使って実現するPayment ChannelのトランザクションはHTLC(Hashed Time-Locked Contracts)と呼ばれています。
複数のPayment Channelにまたがって順番に鍵を明かしていくことで、第三者を経由する送金をアトミックに行うことができるという大きな利点があります。これにより複数のPayment Channelをまたがって行き来する送金(Lightning Network)が実現できるのです。
HTLCは少しずつ異なる方法がいくつか提案されていますが、今回はBOLT(Basis of Lightning Technorogy)と呼ばれるLightning Networkの仕様の中で使用されているスクリプトを見ていきます。
BOLT #3: Bitcoin Transaction and Script Formats
funding transaction(opening transaction)
前回のブログではopening transactionと呼んでましたがBOLTではfunding transactionと呼ばれています(こっちのほうが一般的なようです)。AliceとBobが最初に入金するトランザクションです。
2-of-2マルチシグのP2WSHです。P2SHではなくP2WSHなのはfunding transactionに署名する前にそのoutputを使った有効なcommitment transactionを組み立てる必要があるためです。この辺は前回のブログで解説しています。
commitment transaction
funding transactionのoutputがcommitment transactionのinputになり、さらにcommitment transactionは2つのoutputを持ちます。それぞれto_local(自分宛て)、to_remove(相手宛)と呼ばれています。
「to_local(自分宛て)」
相手の応答が無くなり、タイムアウトを迎えた時に資産を取り戻すためlocking scriptです。
OP_IF
<revocationkey>
OP_ELSE
`to_self_delay`
OP_CSV
OP_DROP
<local_delayedkey>
OP_ENDIF
OP_CHECKSIG
revocationkeyはこのトランザクションをブロードキャストしてしまったときに相手側が取り返すとき使います。
local_delayedkeyは相手が障害等で処理が続行できなくなったとき、タイムアウトまで待って使用します。
OP_IFで分岐していることから分かる通り、unlockするためのスクリプトは2種類あります。
このトランザクションをブロードキャストした場合
<revocation_sig>
1
でunlockできます。
OP_IFの前が1なのでOP_IFの分岐に入ります。OP_IFを展開し、処理されるスクリプトを並べると
<revocation_sig>
<revocationkey>
OP_CHECKSIG
となります。OP_CHECKSIGでトランザクションと署名、署名と公開鍵の関係が検証できればunlock成功です。
revocationkeyは相手に明かしてあるので資産を没収される可能性があります。そのためタイムアウト前にこのトランザクションをブロードキャストするメリットはありません。
タイムアウト後にこのトランザクションをブロードキャストした場合
タイムアウトまで待っても相手が応答しなければ
<local_delayedsig>
でunlockし、自分の資産を取り戻せます。
OP_IFの手前が「0」なので、OP_ELSEの分岐に入ります。OP_IFを展開し、処理されるスクリプトを並べると
<local_delayedsig>
`to_self_delay`
OP_CSV
OP_DROP
<local_delayedkey>
OP_CHECKSIG
となります。
OP_CSVとは・・・
OP_CSVはOP_CHECKSEQUENCEVERIFYと表記されることもあります。
トランザクションのinputの中のtxidや,scriptSigと同じ階層にあるsequenceという項目でタイムアウト(ブロック数)が指定されます。このlocking scriptをunlockするときに、指定された数以上のブロックが作られているかがチェックされ、十分なブロックが作られていないと失敗します。すなわち、このトランザクションがブロックに取り込まれてから一定の時間が継続するまでは使用不可にできます。最初に絶対的なブロック高を指定すると、その前にChannelを閉じる必要がありますが、相対的なブロック数を指定することで一つのPayment Channelを使い続けることができます。
さらにOP_CSVは時間の検証のあと、スタックされた値(ここでは`to_self_delay`)をそのまま返すのでOP_DROPで消しています。
ちなみに`to_self_delay`にはsequenceで指定されたものより小さい値でなくてはいけないという制約があります。sequenceの項目が突然登場することやOP_DROPを必要とすることは一見不自然な仕様に見えますが、BIP-68、BIP-112をBIP-9によってデプロイした名残です。
sequenceは一度使われなくなった項目なので、通常のトランザクションでは全bitで1が立っています。またOP_CSVは元々OP_NOP3だったopcodeを使いまわしているという経緯があります。ソフトフォークでデプロイするためには旧仕様と互換性を保つ必要があったのでこのような仕様になっています。
OP_CSV, OP_DROPを処理したあとスタックはこのようになっています。
<local_delayedsig>
<local_delayedkey>
OP_CHECKSIG
local_delayedkeyはタイムアウトのブロック高のブロックが作られるのを待ってから使用可能になります。
「to_remove」(相手宛て)
タイムロック等は設定しない、P2WPKHで相手側に送るだけのアウトプットなので説明は省略します。
closing transaction
前回のブログではチャンネルのクローズに触れていませんでしたが、クローズに合意すると以降channelのアップデートを停止します。クローズ直前の残高の状態を元にopening transactionをinputにしてAlice, Bobに分配するトランザクションを組み立てます。ブロックチェーン上に残るトランザクションはAliceとBobがP2WSHで入金(funding transaction)、P2WSHからAliceとBobへの分配(closing transaction)の2つということになります。
closeのトランザクションの実物は「lightning networkでビットコインを送金してみた」にも登場します。
まとめ
今回はBOLDの中で使われるPayment Channelのスクリプトを見てみました。
HTLCのいろいろな発展系が考えられており、「報告者」を含めることでスマートコントラクトのオラクルなんかも実現できるそうです。
TumbleBitなど、Layer 2の技術のいろいろな場所で使われており、利用価値の高い仕組みなので今後もこの周辺を調べていきたいと思います。