Lightning Networkの送金処理で使用されている技術【前編】

2018年8月16日(木)
上野 寛

前回Lightning Network BOLTの全体像を解説しましたが、ここからはLightning Networkで使用されている技術について個別に解説していきます。すべてを説明することは難しいため、送金に関する技術を中心に行います。
今回は、Lightning Networkで使用される以下の用語を解説します。

なお、Lightning Networkの技術は日々研究が行われているため、仕様もしばしば追加されています。
今回の記事は、2018年7月上旬の仕様を元にしています。

Lightning Network BOLT

仕様書にあたるLightning Network BOLTは、2018年7月の時点で0~11まであります。
今回の解説でもBOLTへのリンクをいくつか載せているので、詳細についてはそちらを参照してください。

概要
BOLT #0 用語、テーマソング
BOLT #1 Lightning Networkメッセージ
BOLT #2 状態とメッセージ構造
BOLT #3 HTLC、鍵
BOLT #4 ONIONルーティング
BOLT #5 トランザクション
BOLT #6 (削除)
BOLT #7 アナウンスメント
BOLT #8 Noise Protocol
BOLT #9 Features
BOLT #10 mDNS
BOLT #11 請求書

単位

Bitcoinの最小単位は、satoshiです。

Lightning Networkのチャネルで扱う最小単位は、1000分の1satoshi=ミリsatoshiです。仕様書ではmsatoshimsatと略されることもあります。

識別子

Lightning Networkで使用する主な識別子(ID)について解説します。

ノードID (node_id)

Lightning Networkは、Lightning Networkノード(以下、LNノード)同士がネットワーク接続します。また、Bitcoinの送金はアドレスやスクリプトに対して行いますが、Lightning Networkの場合はLNノードに対して行います。
そのため、LNノードを指定するシーンがしばしばあります。

LNノードは、ノードIDという識別子を持ちます。
これは公開鍵に相当する値で、「02」や「03」で始まる数値です。

チャネルID (channel_id)

チャネルIDは、LNノード間で直接通信するときに相手を指定するIDです。
Lightning Networkメッセージ(後述)でチャネル間の通信を行うときに使用します。

IDの値は、チャネルをオープンするときのトランザクションIDと送金先indexを元にした32byteのデータです。
そのため、funding transactionの構成が決定した時点で値が確定します。

ショートチャネルID (short_channel_id)

チャネルIDと同様に、チャネルに対する識別子としてショートチャネルIDもあります。
ショートチャネルIDは、主に送金の転送経路を表すのに使われます。

IDの値はチャネルIDとは関係なく、以下の3つの情報を連結した8byteのデータです。そのため、8byteのデータとせず、3つの値として表すLNノードもあります。

  • [3byte] チャネルオープン時に送金したトランザクション(funding transaction)がマイニングされたブロック番号
  • [3byte] そのブロック中に含まれるトランザクションID列のうち、funding transactionのTXIDが入っているインデックス値
  • [2byte] funding transactionのoutputで、チャネルのマルチシグに送金したインデックス値

funding transactionがブロックに入った後の情報を使うため、値が確定するのはチャネルIDよりも後になります。

計算例

下図は、testnet上でチャネルオープンした際のトランザクションです。
既にクローズした後ですが、

赤枠で囲んだ送金先が2-of-2マルチシグアドレスです。

このトランザクションIDは90315ccd79564dc3087be399e7acf729f8ee47c8140a5749e90dbd6be432d768です。
BitcoinではTXIDなどのハッシュ値を逆順に表現するルール(RPC Byte Order)になっているので、チャネルIDはハッシュ値をそのままの順番(Internal Byte Order)で並び替えた値に2-of-2送金インデックス値をXORした68d732e46bbd0de949570a14c847eef829f7ace799e37b08c34d5679cd5c3190になります。

ショートチャネルIDについては、

  • マイニングされたブロック(Included in Block)は1352592(16進数で14A390)
  • ブロック内のTXIDインデックス値(Block Index)は17(16進数で000011)
  • 2-of-2への送金したインデックス値は0(16進数で0000)

ということから、それぞれを連結して16進数で14a3900000110000となります。

ペイメントハッシュ(payment_hash)

直接目にすることはないかもしれませんが、送金に関してペイメントハッシュというIDがあります。
これは送金してほしいノード(payee)が請求書に載せる値で、送金するノード(payer)はペイメントハッシュを同梱して送ります。

ペイメントハッシュはpayeeが請求書を作成時に生成した乱数(payment_preimage)をSHA256でハッシュ計算した値です。よって、payment_preimageからペイメントハッシュを計算するのは簡単ですが、ペイメントハッシュからpayment_preimageは簡単に求められません。この性質を利用して、HTLCのBitcoinスクリプトにペイメントハッシュを使用し、payment_preimageを持つノードだけがスクリプトを解いてBitcoinを手に入れられるようにしています。

「直接目にすることはないかも」というのは、ペイメントハッシュは請求書(invoice)の中にエンコードされているため、気付かないかもしれないという意味です。
下図はstarblocksで発行された請求書の例ですが、この「lntb....」の中にペイメントハッシュ"b4ef8d6ec9a607b3152a01ce2308f2e5fbe763d7f570f1c5e0fc8dcfe33fa900"が含まれています。

通信

Lightning Networkノード(以下、LNノード)間は、TCP接続します。ポート番号はノードごとに決められますが、通常は9735を使います。この数値は、UNICODEのLIGHTNINGに由来しています。

接続するには、相手ノードのアドレスとポート番号、およびノードIDが必要になります。
仕様では接続する文字列を表示する方法に既定はありませんが、「ノードID@アドレス:ポート番号」のように表現される場合が多いです(ポート番号は9735の場合、省略されることもあります)。
下図は、ACINQ社のLightning Explorer(TESTNET)での表記例です。

02212d3ec887188b284dbb7b2e6eb40629a6e14fb049673f22d2a0aa05f902090e@54.236.55.50:9735

暗号化

ノード間の通信データは、Noise Protocolで暗号化されています。Lightning Network BOLTの仕様では、BOLT#08に記載されています。

LNノード間がTCP接続した後でハンドシェイクして鍵交換を行い、成功したら暗号化したメッセージを送受信できるようになります。

ハンドシェイク

LNノードはTCP接続した後、相手が期待する接続先かどうかを確認するためハンドシェイクを行います。

ハンドシェイクでは、ノードIDの秘密鍵を使って、それ以降の通信で使用する暗号鍵を決定します(送信と受信で鍵は別)。
接続されるノードは、ハンドシェイクによって接続しに来たノードIDがわかるようになっています。
ノードIDの元データが正しくなければハンドシェイクに失敗するため、ノードIDだけ偽って接続することはできません。

Lightning Networkメッセージ

ハンドシェイク後、LNノード間の通信は暗号化します。
方式はChaCha20-Poly1305です(ChaCha20が暗号化で、Poly1305はメッセージ認証符号)。

メッセージ長とメッセージ本体のそれぞれを暗号化し、その2つを結合したものがLightning Networkメッセージです。

最初の暗号鍵はハンドシェイク時に決定しますが、それ以降は500メッセージごとに鍵を変更します。
LNノードが接続している間は、交換するデータが無い場合でも送信するメッセージがあります。そのため、長時間接続したままでも、同じ鍵を使い続けることにはなりません。

メッセージ種別

2018年7月の時点では、以下のメッセージ種別があります。個別のメッセージについては説明を省略します。

Lightning Networkでは、いろいろな鍵データが使用されます。ここでは、各チャネルで使用する鍵データについて説明します。

Bitcoinでは「秘密鍵(private key)」と呼びますが、Lightning Networkではsecretと呼ぶことが多いため、ここでもsecretと呼びます。
公開鍵はpubkey(public keyの略)と呼ぶことにします。

Bitcoinウォレットの鍵管理

Bitcoinでは、以下はほぼ同じ意味です。

  • アドレスの持ち主
  • アドレスから送金できる
  • アドレスの秘密鍵を持っている

そのため、「秘密鍵は絶対に人に見せてはいけないし、紛失してはいけない」と言われます。
Bitcoinは取引所やウォレットアプリが所有しているわけではなく、秘密鍵を持つ人が所有者になるからです。

送金を行う場合、普通は「送金する額」と「お釣り」が発生します。
通常はお釣りのために別のアドレスを作って、そこに送金します。
例えば、Aさんのアドレスに100BTC入っていて、そこからBさんに5BTC送金したい場合は、以下のトランザクションを作ってブロックチェーンに展開します。

  • Bさんアドレス:5
  • Aさんお釣りアドレス:100-5-(手数料)

お釣りをAさんの送金元アドレスと同じにすることもできますが、通常は新しくアドレスを作ります。
つまり、新しい秘密鍵を作ります。
送金してもらう場合も、そのたびに受信用アドレスを新しく作ってもらう場合が多いです。もし何らかの方法で秘密鍵が漏洩した場合でも、そのアドレスが既に送金済みであれば何もできないので、アドレスを新しく作ります。

このように鍵をどんどん作っていくことになりますが、こういうデータが際限なく増えていくと管理が大変です。そこで、「シード(seed)」という秘密鍵を生成する元データを使った「HDウォレット」という方式が主流になっています。
Bitcoinウォレットを最初に作ったとき、単語が並んだデータをバックアップするよう指示されると思いますが、これが「シード」です。
この「シード」とパラメータから秘密鍵を生成するため、ウォレットとしては「シード」だけバックアップしておけば、他の秘密鍵すべてを生成できます。

ウォレットアプリにはPINやパスワードなどありますが、それらはアプリケーションが保存した鍵やデータを保護するためのものです。
シードや秘密鍵が、Bitcoinとして管理するデータになります。

Lightning Networkのウォレットの鍵管理

Lightning Networkではブロックチェーンがないため、Bitcoinとは状況が異なります。

送金をその場で確定させるために、いつでもチャネルをクローズしてBitcoin量を2人に配分するトランザクション(commitment transaction)を作っています。
commitment transactionは、送金途中で相手がいなくなっても取り戻せるようにしたり、相手が裏切って送金を無かったことにしようとすると全額取り戻せるようにしたりするために、HTLCというBitcoinスクリプトへ送金します。
HTLCにpubkeyが複数入っており、該当するsecretを持つ人しか使えないようにしています。

また面倒なことに、commitment transactionは自分が展開する場合と相手が展開する場合があります。

そのため、「自分用のcommitment transaction」と「相手用のcommitment transaction」の2種類があります。
自分用と相手用では条件が変わるので、HTLCの書き方も変わってきます。
例えば、自分にとっては「送金を行う」が相手にとっては「送金を受け取る」になるので、自分のcommitment transactionでは「相手が送金を受け取らずにタイムアウトしたら自分に戻る」という条件が「請求書のpreimageを使えば自分に送金される」という条件になります。

よって、1つのcommitment transactionでは、HTLCで使うsecretの管理が必要で、それが自分用と相手用の2つあることになります。
HTLCで使うpubkeyは最大で5個あります(remotepubkey, delayedpubkey, revocationpubkey, local_htlcpubkey, remote_htlcpubkey)。
これが2つなので10個です。

これが「1つの」commitment transactionについてです。
commitment transactionは、送金のたびに作成します。
前回、送金は「HTLCの追加」「HTLCの反映」の2つに分かれると説明しましたが、これはcommitment transactionをそれぞれ作成するという意味です。
つまり、1回の送金完了では計4つのcommitment transactionを作成します(「HTLCの反映」ではHTLCが消えているので全部の鍵は使いません)。

では、最新の鍵だけ覚えておけば良いかといえば、そうではありません。
相手が裏切ったときの対策が必要です。
「相手が裏切る=古いcommitment transactionをブロックチェーンに展開する」です。
裏切った方が損をするのですが、そのアクションは裏切られた方が起こさないといけません。
展開された古いcommitment transactionと同じ時代のsecretを使ってBitcoinを取り戻します。
すなわち、作成したcommitment transactionのsecretはすべて保持しておかないといけません(相手の鍵については後述のper_commitment_secretだけで良い)。

これは1つのチャネルに関しての話で、チャネルの数だけこのデータがそれぞれ必要です。
これでは、面倒さはともかく、そのまま保存したのではデータ量が膨れ上がってしまいます。

鍵の生成方法

そこで、Lightning Networkでも「シード」を使います。
ですが、BitcoinのHDウォレットとは使い方が異なります。

シード(32byteの乱数)と、インデックス値(初期値は281474976710655)を用意します。
このシードは、チャネルの作成時に決定して、自分だけが持っておきます。
そして、シードとインデックス値からper_commitment_secretという値を作ります。
per_commitment_secretは、インデックス値で表される世代のcommitment transactionで使用するsecretたちのシードに相当します。
そのpubkeyを求めます(per_commitment_point)。

それとは別に、HTLCで使用する鍵の元になるsecretもチャネル作成時に決定します(payment_basepoint_secretdelayed_basepoint_secrethtlc_basepoint_secretrevocation_basepoint_secret)。
HTLCで使用するのは4個の自分で作成した鍵と1個の相手が作成した鍵なので、ここでは4個分作成します。
そして、そのpubkeyを求めます。

ここまでで、per_commitment_secretとその公開鍵であるper_commitment_point、各鍵の元になるbasepoint_secretと公開鍵ができました。
そして、各鍵の元になるbasepoint_secretから、各鍵のsecretを求めます。

1種類だけですが、相手からもらうデータで生成する鍵もあります。取引を行って廃棄したcommitment transactionを使って相手がチャネルを閉じた場合に、すべて取り戻せるようにするための鍵なので、相手から廃棄したper_commitment_secretをもらうことでsecretが生成できるようになっています。

データと生成する鍵の関係は、下図のようになります。

非常に分かりづらかったと思いますが、シードとインデックス値と各basepoint_secretが決定すれば、その世代のcommitment transactionで使用する鍵が求められる、ということさえわかれば良いと思います。
計算式は、BOLT#03をご確認ください。

鍵の保存

自分の鍵については計算して算出できるので良いのですが、HTLCには相手の鍵も使うため、その管理が必要になります。
当然ですが、相手のシードは教えてもらえないため、自分から見ると相手の鍵データには関連性が見えず、そのままデータとして保存するしかないように思ってしまいますが、そこも考慮されています。

BitcoinのHDウォレットはelkremという方式でしたが、Lightning Networkではshachainという方式を採用することで、相手の鍵を効率的に保存できるようになっています。

先ほど、シードとインデックス値からper_commitment_secretを作成すると書きましたが、その作成方法に従うことで、相手からのper_commitment_secretストレージに保存しても固定サイズで収まるようになっています。

おわりに

今回は、Lightning Networkのメッセージや鍵管理について説明しました。
次回は、送金について説明します。

株式会社Nayuta
組み込みソフトウェア開発に十数年取り組む。医療機器や携帯電話の組み込み開発等幅広い経験を持つ。
現在は株式会社Nayutaにて、Lightning Networkノードソフトウェアptarmiganの開発を行っている。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています