Develop with pleasure!

福岡でCloudとかBlockchainとか。

未来の価格に基づいた決済を可能にするDiscreet Log Contracts

Lightning Networkのホワイトペーパーを書いたThaddeus DryjaがこないだDiscreet Log Contracts(直訳すると離散対数コントラクト?)というホワイトペーパーを公開していたので↓見てみる。

https://adiabat.github.io/dlc.pdf

アブストラクト

スマートコントラクトはよくBitcoinのような暗号通貨のシステムで目にするが、金融分野ではまだ広く使われていない。スマートコントラクトを実装、採用にするにあたって、最大のハードルは、スケーラビリティと通貨に関する外部データをスマートコントラクト内から取得する難しさにある。これまでコントラクトのプライバシーは別の問題だった。Discreet Log Contractsは、スケーラビリティとプライバシーの問題に対処し、外部データを提供するオラクル*1への信頼を最小限に抑えるシステムを提案している。またこのコントラクトは外部のオブザーバーがトランザクションログからコントラクトの存在を検出できないように設計されている。

モデル

コントラクトのプロセスに、アリスとボブとオリビアの3者が参加しているとする。このうちアリスとボブはそれぞれ契約相手で、オリビアがオラクルになる。アリスとボブの間に信頼関係はなく、互いに法的な識別情報を知る必要はないが、認証されたチャネルを介して通信でき、互いを永続的に認識できる必要がある。
またアリスとボブはオリビアが署名しブロードキャストしたメッセージを受信できる必要がある。オリビアはアリスとボブを意識する必要はなく、理想的には情報をブロードキャストする以外の接触はない。ブロードキャストする情報は、Bitcoinネットワークでブロードキャストできるほどコンパクトだが、必ずしもBitcoinネットワークにブロードキャストする必要性はない。

DLCプロトコルは多種多様なコントラクトで使用できる。以下の例では未来の通貨価格をベースにしたコントラクトを作り実行する。

コントラクトは水曜日から始まり、水曜日時点では日本円の価値は1000satoshi。コントラクトは金曜日に終了し、その時点の日本円の価値は1050satoshiだったとする。

コミットされたRポイント署名

Discreet Log Contractsでは、Schnorr署名を使うが通常の使い方とは違う。通常ユーザーは秘密のスカラーaを生成し、そのaとベースポイントGからA = aGを計算し、Aを公開鍵として公開する。あるメッセージに署名する際は、aとは別にランダムな秘密のスカラーkを生成しR = kGを計算する。続いて

s = k -h(m, R)a

を計算する。ここでh()ハッシュ関数で、mは署名対象のメッセージ。この署名データは(R, s)になる。

この署名の検証は、与えられた (A, m, R, s)で以下の計算式が成立するか検証することである。

sG = R − h(m, R)A
   = kG − h(m, R)aG

Discreet Log Contractsでは、(A, R)を公開鍵と呼び、署名はsのみとする。方程式は同じだが、Rは署名の一部ではなく公開鍵の一部として使われる。未承認のトランザクションの二重使用を防ぐ仕組みに同様の構造を使用する方法が最近発表されている*2techmedia-think.hatenablog.com

リビアは公開鍵vGであるポイントVを公開している。オリビアはこの公開鍵を複数回使用し、秘密鍵であるvを安全に保つ必要がある。vは異なる当事者が保持する異なる鍵で構成することができる。またオリビアVとは別のポイントR = kGも公開している。kはランダムなnonceでRは1回限りの署名鍵である。これは通常のSchnorr署名で使われているkRと同じだが、Rは署名が計算される前にコミットされる。つまり署名するメッセージはまだ分かっていないが、事前にnonceは選択されそのRは公開されている。

合わせてオリビアRに関するメタデータも公開する。全てのRにはアセットタイプとそれに関する終了時刻がメタデータとして存在する(例:Rを金曜日のマーケットの終値の日本円に関連付ける)。Rは33バイトの長さなので、メタデータRより大きい可能性がある。

Rは公開され既知であるため、署名者がsを計算するより前に任意のmについてsGを計算することができる。

コントラクトの作成

コントラクトはブロックチェーン上に単一の出力として存在し、その出力には契約期間中にコントラクト実行時に支払われる全ての資金がセットされている。(この出力は多くの場合最適化されたものになるが、最適化については後述)

コントラクトをセットアップする前にアリスとボブはまずお互いを見つけて、コントラクトの条件に合意する必要がある。

アリスは金曜日に日本円を買い、ボブは日本円を売る決済を行う。

契約を結ぶには、両者のマルチシグアドレスに資金を入れる(funding output)必要がある。このマルチシグに資金を投入する仕組みはLightning Networkのチャネルのセットアップと大きく変わらない。アリスとボブはマルチシグにロックされた資金のOutPoint(txidと出力のインデックス)に同意する。このトランザクションをブロードキャストする前に、そのトランザクションを入力にした後続のトランザクションを作ることもできる。

続いて、アリスとボブはfunding outputを使用する多数のトランザクションで構成されるコントラクトの作成に移る。この出力は当然ながら1回しか使えないので、作成したコントラクトを構成するトランザクションの内1つのみがブロックチェーン上に記録されることになる。アリスとボブがこの段階ではどのトランザクションがブロードキャストされるものかまだ知らないので、お互い全てのトランザクションについて署名し保存しておく必要がある。

クロージングトランザクションは、契約当初とは異なるクローズ時の価格に基づいて作られる。今回の例では、価格は日本円の価格で、satoshiで表される。そのため、アリスとボブは終値の異なる何千ものトランザクションを作る。

現在開発中のLightning Networkのソフトウェアと同様、当事者はコントラクトの状態について合意するが、双方が作成した各トランザクションを保持する。アリスはボブが署名したトランザクションを保持しており、このトランザクションは2つの出力を持つ。1つはボブへの支払いで、もう1つはアリスもしくはボブへの支払い(アリスが不正を働いた場合のみボブがアリス分も入手する)の出力になる。ボブはこの逆で、アリスが署名したトランザクションで、1つの出力は直接アリスへの支払いをする出力で、もう1つはアリスもしくはボブのどちかに支払うスクリプト(ボブが不正を働いた場合のみボブ分もアリスが入手する)の出力になる。

Lightning Networkではこれらのスクリプトを使ってペイメントチャネルの一貫性を維持し、古い状態のトランザクションをブロードキャストすると相手に全ての資金を支払うことになる。DLCでも同様の仕組みを使うが、ペイメントチャネルと異なるのは、アリスとボブがお互いに過去のコミットメントに使ったシークレットを明らかににするのではなく、オラクルとであるオリビアがシークレットを明らかにする。

コントラクト内のトランザクション

アリスとボブは何千もの署名済みのトランザクションを持ち、それは両者のコンピューター内に保存されている。これらのトランザクションは、入力にfunding outputを持ち、出力は取引相手へのP2PKHと独自のスクリプトハッシュの2つで構成されている。このスクリプトはLightning Networkのチャネルで使われているのと同じスクリプトで、アリスが持つスクリプト

PubAi ∨ (PubB ∧ TimeDelay)

で(∨はorで∧はand)、ボブが持つスクリプト

PubBi ∨ (PubA ∧ TimeDelay)

後者のスクリプトでは、PrivBiを持つユーザーはすぐに出力を使用でき、 PrivAを持つユーザーはある時間経過したら出力を使用できるようになる。Lightning Networkではこれを以前の状態のトランザクションがブロードキャストされた際にそれを取り消すのに使われるが、DLCではオラクルが間接的に正しい状態であると承認したトランザクションのみをユーザーがブロードキャストするよう強制する。

アリスが保持する↑のスクリプトのPubAiはアリスの公開鍵にsiGを加算した以下のスクリプトになる。

PubAi = PubAlice + siG

ここでsiGは↓

siG = R - h(i, R)V

ボブはこの逆で、一定時間待ってアリスの公開鍵PubAに送る出力と待ち時間の無い以下の出力宛に送るトランザクションを保持する。

PubBi = PubBob + siG

検証者はポイントsiGを計算できるが、与えられたiだけではsiの値が何かは分からない。
リビアが署名すると、アリスとボブが既に計算している点の離散対数が明らかになる。

オラクルの署名

リビアの仕事は簡単で、金曜日にマーケットが閉じるのを待って、日本円の終値を観測し予めコミットしたnonceを使ってその数値に署名する。

オラクルは観測した価格をメッセージmとしてセット(この例では1050)し、以下の計算をする。

s = k − h(1050, R)v

水曜日に1000satoshiだった日本円が金曜には1050satoshiに上昇したことになる。
(実際のmの値はハッシュ値だが、ここでは分かりやすくするため、数値をそのまま使用)

ここでアリスとボブが以前トランザクションの鍵を導出するために使ったs1050が明らかになる。

s1050G = R − h(1050, R)V

オラクルがs1050を明らかにすると、PubB1050秘密鍵b + s1050と同じなのでボブはトランザクションの署名に必要な秘密鍵を知り、アリスも同様にPubA1050秘密鍵を知ることになる。

コントラクトの実行

アリスとボブはTX1050をブロードキャストすることで、金曜日の終値に基づく正しい状態でコントラクトを一方的に閉じることができるようになる。トランザクションがブロードキャストされると、すぐその出力の資金を自身のコントロール化のアドレスに送るトランザクションを作成する。
結果、2つのオンチェーントランザクションブロックチェーン上に公開されるが、直接両者の公開鍵ハッシュにコントラクトの実行トランザクションと同額を分配する新しいトランザクションTXggを両者で合意して作成する方がより効率的になる。
またスクリプトハッシュに定義されている期間(TimeDelay)より前に資金を回収しないと相手に全て資金を持っていかれるので注意すること。

アリスとボブのいずれかが途中で実行トランザクションをブロードキャストしたり、(TX1050でなくTX950とか)間違ったトランザクションをブロードキャストすると、スクリプト内のPubAiやPubBiの条件で資金を償還することはできなくなり、もう1つの条件であるTimeDelay経過した後に相手方の公開鍵ロックの条件でしか資金を償還できなくなる。つまり契約ルールに違反すると全ての資金が相手に渡ることになる。

オラクルへの信頼リスク

オラクルであるオリビアが価格を誤って報告する可能性がある。オラクルが誤った報告をした場合、システムの全ユーザーはエラーを識別し、オラクルの使用を停止できる。もしオリビアが2つの異なる価格を公表した場合、オリビアの持つ秘密鍵と二重報告をしようとした特定のコントラクトkの値が明らかになる。
またオリビア自身が契約相手(例えばアリス)だった場合、オリビア秘密鍵を明らかにすること無く、任意のコントラクトを実行できる。オリビアが実行したいTX2をブロードキャストし、続いて以下を計算し

PrivA2 = PrivA + s2

PubA2の出力に署名する。オリビア秘密鍵vを維持しながら、s2を公開せずに署名することになる。これは検知が可能で、詐欺にあったボブは、他の全ユーザーにオリビアのコミットメントと署名の使用を中止するよう、詐欺の証拠を提出することができる。オリビアは参加しているコントラクトを1つ騙すことができるが、同時に全てのユーザーの信頼を失うことになる。

リビアRを公開した後、終値を報告する前にいなくなることも考えられる。こういうケースに対応するため、予めコントラクトが実行される予定日の数日後であれば(タイムアウトすれば)、マルチシグにロックされた資金をそれぞれに払い戻すトランザクションを作っておく必要がある。

複数のオラクルを使用することもできる。2つのオラクルの署名が必要な場合であれば、各当事者は単純にオラクルのsGポイントを各公開鍵に追加する。複数のオラクルを使用することで、オラクルの不正リスクを減らすことができるが、複数のオラクルが報告するデータの一致しないパターンのリスクについて考慮する必要がある。例えばオラクル1は終値を1050と報告し、オラクル2は1049と報告した場合、実行トランザクションを安全に使用することはできず、タイムアウト後に両者に払い戻しが行われる。

もう1つのリスクは、コントラクト内の実質的に全てのポジションを失った当事者が、正当な所有者への資金の移動を遅らせるため、わざと無効なコントラクト実行トランザクションをブロードキャストする可能性があることである。最終的には正しい所有者が資金を回収することができるが、タイムアウトの期間まで待たなくてはならない。ただ、実際に行われることはほとんどないケースだと思われる。

最適化

いくつかの最適化によりデータ量と計算量を減らすことができる。システムを実装する際にはまた多くの最適化方法が見出されると思うが、以下にいくつかの基本的なアイディアをリストアップする。

R値の基数と指数

アリスとボブはコントラクトを作る際、オリビアが署名する可能性がある全てのメッセージmiについて署名を互いに送信し保持する必要がある。価格の候補は非常に多く、何千もの署名を検証し保存する必要がでてくる。ほどんどの場合、ノックインとノックアウトの価格が、それを下回るもしくは上回るとどちらか一方がコントラクトの全ての資金を受け取ることになる。例えば日本円の価格が10 satoshi以下になった場合、アリスとボブは価格が4 satoshiであろうが5 satoshiであろうが関係なくボブが全ての資金を手にすることに合意する。同様に価格が5000を超えると、それが6000か7000かに関わらず全ての資金をアリスが手にする。

価格のレンジは数桁以上にわたるため、アリスとボブはこれらの極端な範囲での取引を少なくすることで、ノックインとノックアウトの価格を最適化することができる。これはオリビアがRmantissaとRexponentという2つのRの値にコミットすることで可能になる。オリビアは価格を表す2つのメッセージに署名することを約束する。1050に署名する代わりにRmantissa仮数部)を使用して.050に署名し、Rexponent(指数部)を使用して3に署名する。小数点の基数と仮数には暗黙的に1が指定される。1.050 ∗ 103 = 1050

アリスとボブはsmantissaGとsexponentGのポイントを加算することで、同じようにトランザクションを構築する。オリビア仮数と基数のメッセージのペアに署名すると、sの値が明らかになる。Rexponentが4,5,6のような場合であれば、仮数部は無視できるレベルの数値になる。その場合Rexponentのみでコントラクトを作ればいいし、詳細な値まで含める場合はRexponentとRmantissa両者を必要とするコントラクトになる。どちらの粒度でコントラクトを作成するかは、コントラクト作成前に合意しておく必要がある。

↑の例ではRが2つだが、Rの数を3,4と増やすことで粒度を細かくすることもできる。また10は最適な基底ではなく、基底には2を使うのが良い選択になるだろう。

チャネル内のコントラクト

コントラクトはLightning Networkのチャネル内で作ることもできる。コミットメントトランザクション内の直接の送金とHTLCの送金の2つの出力に加えて、コントラクトの出力を追加する。あとはコントラクトの結果についてオンラインで両当事者が合意すれば、コントラクトの実行結果に従いお互いの残高を更新した新しいコミットメントトランザクションを作成し交換すれば良い。取引相手が非協力的な場合は親チャネルを閉じ、オラクルが提供した値を使ってすぐにコントラクトを終了させることができる。

所感

  • 予測される未来の値をベースにした決済を執行できるという意味で、暗号通貨を使用するユースケースの拡張につながる面白いアイディアだと思う。
  • トラストポイントとしてオラクルの存在があるモデルで、オラクルが厳密に不正をできない仕組みにはなっていないが、不正の検知は可能で、不正を行ったオラクルは以降信頼されずオラクルとして使われることはない。

*1:外部データを提供するエンティティのこと

*2:http://eprint.iacr.org/2017/394.pdf