Develop with pleasure!

福岡でCloudとかBlockchainとか。

コンテンツを復号可能な鍵をアトミックに交換できるBitStream

BitVMやZeroSyncの開発者であるRobin Linusが先日公開したBitStreamのペーパー↓

https://robinlinus.com/bitstream.pdf

分散型のコンテンツホスティングネットワークに対して(最近ユーザーが増えてるNostrとか)、持続可能なインセンティブを持たせるため、コンテンツのダウンロードに対して支払いを求めるPay to Downloadというアプローチを提案している。

コンテンツプロバイダーは、以下のプロトコルで支払いと引き換えにコンテンツをユーザーに提供する。

  1. プロバイダーは、検証可能な形でコンテンツを暗号化し、ユーザーに送信する。
  2. ユーザーは、提供された暗号化データが復号可能かどうか検証する。
  3. ユーザーは、HTLCを使ってプロバイダーから復号鍵を購入する。
  4. プロバイダーがHTLCの資金を入手すると、そのトランザクションからユーザーは復号鍵を得られる。つまりHTLCのプリイメージが復号鍵。
  5. ユーザーは得られた復号鍵を使ってコンテンツを復号する。

復号鍵の入手と支払いがHTLCによってアトミックに行われるけど、HTLCのプリイメージで本当に復号できるのかをどう担保しているのか見ていく。

File IDの導出

まず、コンテンツは固定サイズのチャンクに分割され、各チャンクのハッシュをリーフノードとしたマークルツリーを構成する↓

https://gyazo.com/409eb16487c16b611e425a9599968c70/max_size/1000

このマークルツリーのルートハッシュが、このコンテンツを識別するための一意のFile IDになる。

ユーザーは、このFile IDを指定してプロバイダーに対象のコンテンツを要求する。

暗号化

ユーザーからコンテンツを要求されたプロバイダーは、コンテンツを暗号化する。

コンテンツは以下のようにXORを使用した単純なワンタイムパッドで暗号化される(なので各チャンクは↓のプリイメージのハッシュ値と同じ固定長である必要がある)。

  1. まず暗号化に使用するランダムな値を選択する。これがHTLCのプリイメージになる。
  2. コンテンツの各チャンク毎(チャンクのインデックスをiとする)に、プリイメージとインデックスのハッシュ値とでビット単位のXOR演算を行うことで各チャンクを暗号化する。つまり暗号化されたチャンクは {E_i = encrypt(chunk_i) = chunk_i \oplus H(プリイメージ || i)}

暗号化された各チャンクは、再度XOR演算を行うことで( {chunk_i = E_i \oplus H(プリイメージ || i)})、復号できる。

暗号化されたチャンクとインデックスにプリイメージが揃えば復号できるため、HTLCに設定するペイメントハッシュは

ペイメントハッシュ = H(プリイメージ)

となる。

Encrypted IDの導出

続いて、暗号化された各チャンクと、元の各チャンクのハッシュ値のペアをリーフノードとしてマークルツリーを構成する↓

https://gyazo.com/464fa766e293830920a24e4091df0428/max_size/1000

このマークルツリーのルートハッシュがEncrypted ID。

コンテンツの購入

そして、プロバイダーは以下のデータをユーザーに送信する。

  • 各チャンクのハッシュ値 {H_i}
  • 各チャンクを暗号化したデータ( {E_i}
  • ペイメントハッシュを含むLNインボイス
  • Claim = Encrypted ID || ペイメントハッシュとし、このClaimをメッセージとしたデジタル署名

データを受信したユーザーは以下を行う。

  • 各チャンクのハッシュ値 {H_i})からマークルツリーを構成し、そのルートハッシュがFile IDと一致するか検証する
  • 各チャンクの暗号化データとハッシュ値からマークルツリーを構成しルートハッシュ(Encrypted ID)を計算する。
  • Claimのデータをメッセージとして、プロバイダーが提供したデジタル署名が有効か検証する。
  • LNインボイスを確認

検証をクリアしたら、ユーザーはLNインボイスに対して支払いを行う。

コンテンツの復号

プロバイダーがプリイメージを使用してLN支払いを受け取ると、ユーザーはプリイメージを入手できるので、プリイメージを使用して、暗号化されたチャンクを復号する。

そして、復号した各チャンクのハッシュ値が、元々受け取っていた {H_i}と一致するか検証する。

Fraud Proof

復号したデータの検証が失敗した場合、ユーザーにとって以下のデータがプロバイダーの不正を証明するFraud Proofになる。

  • Claimをメッセージとしたプロバイダーの署名
  • ペイメントハッシュのプリイメージ
  • 正しく復号できなかったチャンク {(E_i, H_i)}のペアがEncrypted IDのマークルツリーに含まれていることを証明するマークルプルーフ

ここで証明しているのは、プロバイダーがコミットしたコンテンツ(チャンク)のハッシュ値が、復号したチャンクのハッシュ値と一致しないこと。

Bond Contract

支払いはしたけれど復号できなかった場合に、Fraud Proofを使ってプロバイダーにペナルティを与えるのがBond Contractの役割。Bond Contractは、

  • Claimに対するプロバイダーの署名の検証
  • ペイメントハッシュとプリイメージの検証
  • 復号後のハッシュが一致しなかった {(E_i, H_i)}のペアのEncrypted IDツリーに対するマークルプルーフの検証
  • 暗号化されたチャンク {E_i}を復号(XOR演算)して {H_i}と一致しないことの検証

を行い、これをパスするとプロバイダーにペナルティを与える。

実際にLiquidのtestnetでデモされたトランザクションが↓

https://blockstream.info/liquidtestnet/tx/2e50abbaabd474833d2b61863058e5d1ef93327680428ba5e8d665d983c2dddb

プロバイダーがデポジットした資金をOP_RETURNで焼却している。具体的なコントラクトの中身は↓

https://github.com/RobinLinus/BitStream/blob/master/contract/burn_contract.md

上記の検証を行うために、Liquidの以下の機能を活用している:

  • 任意のメッセージ(Claim)に対する署名検証を行うOP_CHECKSIGFROMSTACK
  • マークルパスの検証(マークルプルーフから部分的にマークルツリーを再構築してルートハッシュを比較する)にOP_CATおよびOP_MODOP_DIV
  • チャンクの復号にOP_XOR

個人的に以下のコードブロックの連続で(ループができないので、必要と思われる数分このコードブロックが続く)、マークルツリーの復元してるのが面白かった。対象ノードのインデックスに対してOP_DIVでこの次の計算で使用する該当ノードのインデックスを算出し、OP_MODで左右どちらか判定して必要に応じて、ノードの左右を入れ替えて、連結して内部ノードのハッシュ値を計算してるっぽい。

...
// Loop begin
OP_DEPTH 
OP_1SUB
OP_IF

    OP_FROMALTSTACK
    OP_DUP
    <2>
    OP_DIV
    OP_TOALTSTACK
    <2>
    OP_MOD

    OP_NOTIF
        OP_SWAP
    OP_ENDIF

    OP_CAT
    OP_SHA256

OP_ENDIF
...

Bitcoinで実現するには?

↑はLiquidのL-BTCを使ったBond Contractだけど、これをBitcoinでやろうとすると必要なopcodeが無いので現状はできない*1。ペーパーでは、最低限OP_CATだけ導入すればBitcoinでも構成できると提案している。他のopcodeについての対応は↓

  • XORの処理については、32ビットまでのデータであれば既存の算術opcodeを使ってエミュレートする。
  • マークルプルーフの検証に使用するリーフのインデックスについては、bit文字列としてアンロックスクリプトで指定することで、OP_MODをエミュレートする。
  • OP_CHECKSIGFROMSTACKはどうするの?と思ったけど、Andrew Poelstraが以前紹介していたトリック↓を使って、OP_CATとSchnorr署名でエミュレートするみたい。

techmedia-think.hatenablog.com

BitStreamでできないこと

Fraud Proofによる不正の防止が組み込まれたBitStreamを利用することで、トラストレスな形で復号可能なコンテンツの鍵を支払いとアトミックに交換することができる。ただ、復号は保証されるが、その結果がユーザーが期待するコンテンツであるのかどうかまではBitStreamでは保証されない。そういう意味では、(Zero Knowledge Contingent PaymentやBitVMのようなステートメントの検証がないため)プロバイダーへのトラストが残る。

*1:現状のBitcoinではOP_CAT、OP_XOR、OP_DIV、OP_MODは無効化されており、OP_CHECKSIGFROMSTACKは存在しない