はじめに
Discreet Log Contracts(以下、DLC)は、Bitcoinにおける未来予測市場で使う事の出来る技術です。
2017年8月にTadgeによって発表されました、DLCはLightningとほぼ同じ仕組みで動きます。
最近、Lightningが実用化されつつあり、ワークショップなども開催されているようです。
Lightningを理解していれば案外簡単に利用する事が可能であると思います。
ここでは、実際に使用可能なDLCのTransactionについて説明します。
また、実際にregtest環境で演習できるようにしてあります。
簡単な概要
アクターは、大きくユーザ(User)とオラクル(Oracle)が必要です。
ユーザ(User)は、アリス(Alice)とボブ(Bob)とします。
オラクル(Oracle)は、オリビア(Olivia)とします。
これらの、ユーザ同士のマッチングはすでにできているものとします。
(ただし、お互いの素性を知る必要はありません。)
オラクル(Oracle)はメッセージから契約Rポイント(Committed R-point)を計算可能にする為、オラクル自身の公開鍵(楕円曲線の点)と、未来の契約に対するナンス(Nonce、楕円曲線の点)を公開しています。
※:ナンスは、契約毎に異なる必要があります。
アリスとボブは、未来のメッセージに対して契約を行います。
ファンド(Fund Transaction)から、契約実行取引(Contract Execution Transactions , CET)をメッセージ数分作成して、それらの署名をお互いに交換します。
(CETは、アリスとボブとで非対称なTransactionになります。)
未来のメッセージが確定したら、オラクル(Oracle)が確定したメッセージに対して署名値を公開します。
アリスとボブのどちらかが、その署名値を使用して、メッセージに対応した契約実行取引(CET)を送信します。
さらに、契約実行取引(CET)から自身のアドレス(P2WPKH)に送って完了となります。
契約例(DLC Sample)
ここでの例として、オラクルは未来(N日後)の天気について、晴(Fine)か雨(Rain)に署名して公開します。
アリスとボブは、お互い1BTC
づつ出しあい、晴(Fine)であればアリスが1.5BTC
ボブが0.5BTC
雨(Rain)であれば、アリスが0.5BTC
ボブが1.5BTC
とする契約を結ぶ事とします。
オラクル(Oracle)
オラクル(Oracle)は、公開鍵(自身の公開鍵と未来に対する公開鍵の2つ以上)を公開し
未来のメッセージが確定したら、署名値を公開します。
各ユーザは、公開されている公開鍵から各メッセージに対するナンスを計算します。
\begin{align*}
P_o = x_oG & \quad \text{: Oracleの公開鍵} \\
R_n = k_nG & \quad \text{: 未来のナンス} \\
\{ m_{\text{Fine}} , m_{\text{Rain}} \} & \quad \text{: メッセージ}\\
\end{align*}
契約Rポイント(Committed R-point)の求め方
オラクルが公開している情報から、ユーザ(アリスとボブ)が作成し、契約に利用します
\begin{align*}
& R_{\text{Fine}} =R_n + h(R_n\ ||\ m_{\text{Fine}})P_o \\
& R_{\text{Rain}} =R_n + h(R_n\ ||\ m_{\text{Rain}})P_o \\
\end{align*}
署名値の求め方
未来のメッセージが確定したら、以下のどちらかの署名値を計算して公開します。
(晴であれば上、雨であれば下、一方のみ公開)
\begin{align*}
& s_{\text{Fine}} =k_n + h(R_n\ ||\ m_{\text{Fine}})x_o \\
& s_{\text{Rain}} =k_n + h(R_n\ ||\ m_{\text{Rain}})x_o \\
\end{align*}
DLCの手順
アリスとボブの公開鍵を以下とします。
\begin{align*}
P_a = x_aG & \quad \text{: アリスの秘密鍵と公開鍵} \\
P_b = x_bG & \quad \text{: ボブの秘密鍵と公開鍵} \\
\end{align*}
ファンド
アリスとボブはファンドを作成します。
BitcoinのTransactionは以下のとおりです。
このTransactionは、署名値を設定する前にtxid
が決定されなければならないので、
Inputs
に設定されるUTXO
はすべて、Segwitアドレスである必要があります。
Version 0x02000000
Inputs <Alice's UTXO 1>
<Alice's UTXO : (option)>
<Alice's UTXO n (option)>}
<Bob's UTXO 1>
<Bob's UTXO : (option)>
<Bob's UTXO m (option)>
Outputs <Fund Script (P2WSH)>
<Alice's change (P2WPKH : option)>
<Bob's change (P2WPKH : option)>
Locktime 0x00000000
FundのScriptは以下のとおりです。
OP_2
<Alice's public key>
<Bob's public key>
OP_2
OP_CHECKMULTISIG
契約実行取引(Contract Execution Transactions , CET)
アリスとボブは、メッセージ分の署名値を作成しお互いに交換します。
今回の例だと、2つの署名値を交換します。
ここで、オリビアが公開している値から算出した契約Rポイントを使用します。
アリスが署名し、署名値をボブに渡す為のTransaction
メッセージ:晴(アリス⇒ボブ)
Version 0x02000000
Input <Fund Output (Fund Transaction : 0)>
Outputs <CET Script (P2WSH)> : 0.5 BTC
<Alice's public key (P2WPKH)> : 1.5 BTC
Locktime 0x00000000
OP_1
OP_EQUAL
OP_IF
<Bob's public key> + <R_{Fine}>
OP_ELSE
144(Delay)
OP_CHECKSEQUENCEVERIFY
OP_DROP
<Alice's public key>
OP_ENDIF
OP_CHECKSIG
メッセージ:雨(アリス⇒ボブ)
Version 0x02000000
Input <Fund Output (Fund Transaction : 0)>
Outputs <CET Script (P2WSH)> : 1.5 BTC
<Alice's public key (P2WPKH)> : 0.5 BTC
Locktime 0x00000000
OP_1
OP_EQUAL
OP_IF
<Bob's public key> + <R_{Rain}>
OP_ELSE
144(Delay)
OP_CHECKSEQUENCEVERIFY
OP_DROP
<Alice's public key>
OP_ENDIF
OP_CHECKSIG
ボブが署名し、署名値をボブに渡す為のTransaction
メッセージ:晴(ボブ⇒アリス)
Version 0x02000000
Input <Fund Output (Fund Transaction : 0)>
Outputs <CET Script (P2WSH)> : 0.5 BTC
<Bob's public key (P2WPKH)> : 1.5 BTC
Locktime 0x00000000
OP_1
OP_EQUAL
OP_IF
<Alice's public key> + <R_{Fine}>
OP_ELSE
144(Delay)
OP_CHECKSEQUENCEVERIFY
OP_DROP
<Bob's public key>
OP_ENDIF
OP_CHECKSIG
メッセージ:雨(ボブ⇒アリス)
Version 0x02000000
Input <Fund Output (Fund Transaction : 0)>
Outputs <CET Script (P2WSH)> : 1.5 BTC
<Bob's public key (P2WPKH)> : 0.5 BTC
Locktime 0x00000000
OP_1
OP_EQUAL
OP_IF
<Alice's public key> + <R_{Rain}>
OP_ELSE
144(Delay)
OP_CHECKSEQUENCEVERIFY
OP_DROP
<Bob's public key>
OP_ENDIF
OP_CHECKSIG
契約実行(Contract Execution)
未来のメッセージが晴(Fine)であったとします。
また、アリスが契約を実行する事とします。
アリスが送信するCETは以下のとおりです。
Version 0x02000000
Input <Fund Output (Fund Transaction : 0)>
Outputs <CET Script (P2WSH)> : 0.5 BTC
<Bob's public key (P2WPKH)> : 1.5 BTC
Locktime 0x00000000
OP_1
OP_EQUAL
OP_IF
<Alice's public key> + <R_{Fine}>
OP_ELSE
144(Delay)
OP_CHECKSEQUENCEVERIFY
OP_DROP
<Bob's public key>
OP_ENDIF
OP_CHECKSIG
不正防止用にScriptが組まれているので、遅延(Delay)時間以内(ここでは、144ブロック)に
自身のアドレスに送る必要があります。
Version 0x02000000
Input <CET Output (Contract Execution Transactions : 0)>
Outputs <Alice's public key (P2WPKH)> : 1.5-fee BTC
Locktime 0x00000000
合成された公開鍵に対応する、秘密鍵は秘密鍵の和で求めます。
したがって、ここでの秘密鍵は以下となります。
\begin{align*}
x_{a+Fine} = x_a + s_{Fine}
\end{align*}
これによって、計算した署名値(<signature>
)を使用してwitness
を設定します。
witness[0] <signature>
witness[1] 01
witness[2] <CET Script>
署名値は、合成された秘密鍵を使うので、遅延(Delay)はあまり重要ではありません。
決済取引(Settlement Transactions)が送信するまでの時間が稼げれば良いのです。
演習(Exercise)
Bitcoinをregtestで起動します。
bitcoind -regtest -printtoconsole
Transactionを作成する為に、DLC Transactionをブラウザで開いてください。
はじめてregtestで起動する場合、CSVを有効にする為、432ブロック進めてください。
bitcoin-cli -regtest generate 432
ファンド
ファンドのInput
は全てSegwitアドレスである必要がある為、「Preparation」のコマンドを実行してください。
sendtoaddress
コマンドにより、txidが2つ出てくるので、voutがどちらか調べます。
bitcoin-cli -regtest getrawtransaction <txid of Alice> 1
bitcoin-cli -regtest getrawtransaction <txid of Bob> 1
txidの順番どおり、Alice、BobのUTXOとなります。
「UTXOs of Alice and Bob」に、txid
とidx
を設定します。
「ファンド作成」⇒「契約実行取引の署名交換」⇒「ファンド送信」の流れになります。
「Fund Transaction (with signature)」のhexstring
は署名がついていますので確認してみてください。
bitcoin-cli -regtest decoderawtransaction <hexstring>
outputs
の0番目にファンドの出力があります。
ファンドを送信します。
bitcoin-cli -regtest sendrawtransaction <hexstring>
bitcoin-cli -regtest generate 1
送信ができたら、txid
が出力されます。
※:エラーが出る場合は、UTXOsの設定が間違っている可能性があるのでもう一度確認してみて下さい。
これがブロックに入ったら契約完了となり、Oliviaが署名値を出すまで待つ事になります。
AliceとBobのどちらがCETを送るか、またメッセージは、晴(Fine)と雨(Rain)のどっちであるかを選択します。
CETのhexstring
がありますので、確認してみてください。
bitcoin-cli -regtest decoderawtransaction <hexstring>
outputs
の0番目が「witness_v0_scripthash」、1番目が「witness_v0_keyhash」となります。
1番目は、相手側のアドレスになっています。
inputs
はファンドだけですが、txinwitness
の最後はScriptなので内容を確認してみます。
2-of-2のMultisigである事がわかります。
bitcoin-cli -regtest decodescript <hexstring of script>
CETを送信します。
bitcoin-cli -regtest sendrawtransaction <hexstring>
bitcoin-cli -regtest generate 1
次に、CETから自身のWallet送信します。
bitcoin-cli -regtest decoderawtransaction <hexstring>
outputs
の0番目が「witness_v0_keyhash」で、自身側のアドレスになっています。
inputs
はCETだけです、txinwitness
の最後はScriptなので内容を確認してみます。
CET Scriptである事がわかります。
bitcoin-cli -regtest decodescript <hexstring of script>
送信します。
bitcoin-cli -regtest sendrawtransaction <hexstring>
bitcoin-cli -regtest generate 1
これで、DLCが完了します。
最後に
基本的なTransactionはこれで終わりですが、実際はOracleと連絡がとれなくなった場合を考慮して、ファンドからお互いに払戻(Refund)トランザクションが必要になります。
払戻トランザクションは、ファンドからお互いに払戻すトランザクションですが、LockTime
をOracleが公表する時間より十分後に設定する必要があります。