Blockstreamが公開しているSidechainのホワイトペーパーのAppendixにFederated Pegを行う際のP2SHアドレスの導出スキームが定義されている。このFederated Pegの仕組みは同じくBlockstreamが公開しているサイドチェーンの実装Elements Alphaでも利用されている。
techmedia-think.hatenablog.com
楕円曲線の準同型性を利用したこのアドレス導出のスキームの実装がどうなってるのか見てみる。
Federated Pegのコンセプト
現在のBitcoinのスクリプトにはSPV proof検証が行えるような機能は無いため、Bitcoinとサイドチェーンをペグする方法としてはFederated Pegを利用する方法がとられている。OP_SIDECHAINPROOFVERIFY
のようなソフトフォークも考えられているみたいだけど、最近のSegwitのデプロイ状況を見てもわかるようにソフトフォークといっても合意を取るのは難しいので、ソフトフォーク無しでペグするのが現状は妥当なアプローチだと思う。
Federated Pegでは、信頼できる連合の職員にスクリプトを評価させることで、Bitcoinのスクリプトを拡張して実現しようとしていた機能を外部的に実装している。そのため連合の職員というトラスポイントが発生する。
ホワイトペーパーに記載されている例より
連合職員5人の内3人の署名が必要なケースを考えた場合、連合はsecp256k1の楕円曲線上の点(公開鍵)P1、P2、P3、P4、P5と、redeemscriptのテンプレート3 x x x x x 5 OP_CHECKMULTISIG
を持ち、それらはサイドチェーンの全参加者に知られている。ユーザはfederated pegを使ってサイドチェーン上でコインを有効にするため、以下のスキームでクロスチェーンP2SHアドレスを導出する。
この導出スキームはBIP-32で使われているのと同じ準同型の手法をベースにしていて、第三者がリンク不可能なアドレスを生成でき、pay-to-contractトランザクションと同じ構成でもある。
アドレスを生成すると、そのアドレス宛てにコインを送る。コインを送ったユーザは連合の職員にnonceとscriptPubkey、SPV proofに提供すると(職員がBitcoinのブロックチェーン上の支払いを確認するのに必要)、送ったコインと同額のコインをサイドチェーン上で入手することができる。Bitcoinとサイドチェーン間のコインの転送は、標準のP2SHアドレスへ支払い、任意のScriptPubKeyに対して支払われるので、マルチシグアドレスをサポートしているBitcoinサービスであればすぐにでもfederated pegを利用してサイドチェーンへのコインの支払い、サイドチェーンからのコインの受取ができる。
federated pegのアプローチではチェーン間の移動の際に連合の職員というトラストポイントが発生するが、Bitcoinになんら変更を加える必要がないのと、サイドチェーンの利用を限られたユーザのみにしぼりたい場合も、連合の職員によってそういった制御をすることが可能になるといったメリットもある。
実装
実はこれをRubyで実装したものがbitcoin-rubyの一部にある↓
https://github.com/lian/bitcoin-ruby/blob/master/lib/bitcoin/contracthash.rb
ので、身近なRubyの実装でみていく。
アドレスの導出
コインをBitcoinからサイドチェーンに送る際のP2SHアドレスを計算する。
bitcoin-rubyでこれを実装しているのがBitcoin::ContractHash#generateで
引数に渡すのが↓の3つ
- redeem_script_template
redeemscriptのテンプレートで↑のホワイトペーパーでは、3 x x x x x 5 OP_CHECKMULTISIG
の部分。 - payee_address
サイドチェーン上でコインを受け取るアドレス(ホワイトペーパーではScriptPubKey) - nonce_hex
128bitの乱数
この実装が↑のホワイトペーパーに記載されているアドレス導出スキームの実装にあたり、以下のプロセスでP2SHアドレスを生成している。
nonce
とpayee_address
を組み合わせてdata
を生成するredeem_script_template
(3 x x x x x 5 OP_CHECKMULTISIG
)のマルチシグの各公開鍵=楕円曲線上の点に対して、1で生成したdata
を加算して新しい点=公開鍵を生成する。redeem_script_template
の各公開鍵の部分を、2で生成した新しい公開鍵に置き換えたP2SHスクリプトを生成する。- 3で生成したP2SHスクリプトから生成したP2SHアドレスとその
redeem_script
とnonce
を返す。
ユーザは生成されたP2SHアドレスにコインを送付する。
連合の職員の秘密鍵
ユーザーは職員にpayee_address
(ホワイトペーパーではScriptPubKey)とnonce
を渡す。連合の職員は渡されたpayee_address
とnonce
を使って秘密鍵を入手する。
この処理を実装しているのがBitcoin::ContractHash#claimで、
引数に渡すのが↓の3つ
- private_key
元々公開していた公開鍵の秘密鍵 - payee_address
ユーザから渡された支払先のアドレス(ホワイトペーパーではScriptPubKey) - nonce
ユーザから渡されたnonce
↑でユーザがコインを送付したアドレスのロックを解除するための秘密鍵を↑のclaimメソッドで生成している。ユーザから渡されたpayee_address
とnonce
から元々の公開鍵に加算されたdata
を計算し、秘密鍵にそのdata
を加算した新しい秘密鍵を生成している。
ポイントとなるのは、ユーザは公開されている職員の公開鍵に対してnonce
とアドレスから生成したdata
を加算した公開鍵を生成しており、職員はそれに対応する秘密鍵を同じく秘密鍵にdadta
を加算して入手してる点で楕円曲線の準同型性を利用している。ユーザがコインを送付するマルチシグは連合のマルチシグの各公開鍵にデータを加算したマルチシグであるため、このアドレスへの支払いがサイドチェーンへの支払いになっていることを第三者がリンクすることはできない。