Bitcoin決済をしたことを証明する支払いの証明(Proof of Payment)方法について定義しているのがBIP-120↓
https://github.com/bitcoin/bips/blob/master/bip-0120.mediawiki
Bitcoinによる支払いが行われたことを条件にサービスなどを提供するケースって結構あるんじゃないかなー。
(BIP-120自体はコンセンサスなどとは無関係なApplications
レイヤーのBIPなので、デプロイなどは無い。)
動機
支払いの証明(Proof of Paymant = PoP)を利用することで以下のようなユースケースが考えられる。
- ホテルなどで既に料金を支払っている場合に、そのPoPがドアの鍵になる。
- オンラインのビデオサービスでビデオの代金を支払っていれば、どのデバイスでも視聴できる。
- 事前に支払い済みの広告。広告の掲載期間内であればいつでも、PoPを使って新しいコンテンツを広告としてアップロードできる。
- PoPを使って有料サイトにログインする。
- 毎月支払いをする駐車場や車の認証をPoPを使って行う。
- 全参加者が同じアドレスに支払いをする宝くじで、勝者はそのアドレスへ支払いをしたトランザクションの中から選択される。勝者はそのトランザクションのPoPと賞金を交換する。
Proof of Paymentを使うと個人情報(ユーザー名やパスワード、メールアドレス)などを必要とせず、こういったユースケースを実現できる。
論拠
PoPを実現する上で必要な特性:
- PoPは必要に応じて生成する。
- 盗難リスクを避けるため基本的に一度しか使用できない。
- (P2SHやP2PKHなどの)スクリプトタイプに関係なく、支払いのためをPoPを作成できる必要がある。
- 実証済みのトランザクションの全入力のロックを解除するのに、十分な資格を持っていることを証明する必要がある。
- 簡単に採用できるようウォレットとサーバーの実装は簡単でなければならない。
支払いを証明するための現在の方法:
- BIP-70では、リクエストを満たすトランザクションとともに、PaymentRequestが何らかの証明をするが、1,2もしくは4は満たしておらず、BIP-70では3しかできない。そのため証明を要求/提供する標準的な方法は定義されていない。標準化すれば5を満たせる。
- サーバーが生成したメッセージに、トランザクションに署名するのに使う秘密鍵を使って署名する。これは1と2を満たすかもしれないが、おそらく3は満たせない。これも標準化はされていない。4は設計されていれば満たせる。
仕様
データ構造
トランザクションTの支払いに対する証明(PoP(T)
とする)は、Tの全ての入力をアンロックするのに必要な情報を所有していることを証明するデータでもある。PoP(T)
の入力は基本的にTと同じトランザクション構造(同じ入力を同じ順で持つ)をもつデータで、入力データのうちシーケンス番号だけは元と違い0がセットされている。またpop output
と呼ばれるコインの量が0の出力を1つ持つ。このpop output
の形式は↓
OP_RETURN <version> <txid> <nonce>
フィールド | バイト数 | 内容 |
---|---|---|
version | 2 | バージョン。リトルエンディアンで現在は0x01 0x00 |
txid | 32 | 証明するトランザクションのtxid |
nonce | 6 | ランダムデータ |
PoPのロックタイムは必ず499999999
にセットする必要がある。これは誤ってPoPがBitcoinのP2Pネットワークに流れても、ブロックに取り込まれることが無いようにするため。シーケンス番号がffffffff
だとロックタイムを無効にするので、シーケンス番号に0をセットしているのもそのため。この仕様では全入力のシーケンス番号を0にするようにしている。ロックタイムを有効にするのには1つでも十分だけど、シンプルにするため。
TとPoP(T)
の構造はそれぞれ以下のようになる。
T +------------------------------------------------+ |inputs | outputs | | Value,Sequence | Value,Script | +------------------------------------------------+ |input0 1,ffffffff | 0,pay to A | |input1 3,ffffffff | 2,OP_RETURN <some data> | |input2 4,ffffffff | 1,pay to B | | | 4,pay to C | +------------------------------------------------+ PoP(T) +-------------------------------------------------------------+ | inputs | outputs | | Value,Sequence | Value,Script | +-------------------------------------------------------------+ |input0 1,00000000 | 0,OP_RETURN <version> <txid> <nonce> | |input1 3,00000000 | | |input2 4,00000000 | | +-------------------------------------------------------------+ | lock_time=499999999 | +-------------------------------------------------------------+
PoPはBitcoinのトランザクションと同じ署名プロセスで署名される。
nonce
は盗まれたPoPの使用を困難にするための要素で、サーバーがPoPのリクエスト毎に新しいnonce
を生成することで、盗まれたPoPを無効化する。
プロセス
- 最初にProof of Paymentのリクエストがサーバーからウォレットに送られる。このPoPリクエストには、以下が含まれる。
- ウォレットはサーバーの情報からトランザクションを特定する。ウォレット単体で特定できない場合は、1-3のヒントをユーザーに確認し、一致するものを選択してもらう。
- ウォレットは特定したトランザクションの未署名のUPoPを作成し、ユーザーに署名するか尋ねる。
- ユーザーは署名するか確認する
- ウォレットはUPoPに署名しPoPを作成する
- PoPを1の宛先に送信する
- サーバーは受信したPoPを検証し、その結果を
valid
かinvalid
で返す - ウォレットは何らかの方法でユーザーにレスポンスを表示する。
備考
PoPの検証
サーバーはPoPを検証し、valid
かinvalid
を返す必要がある。この検証プロセスの概略を以下に示す。いずれかのステップでも失敗すると検証は途中で中止され、invalid
が返される。
- PoPのフォーマットをチェックする。入力が使用済みであることを除いて、通常のトランザクションの検証をパスする必要がある。
- ロックタイムが499999999かチェックする。
- 出力は1つだけかチェックする。この出力のコインの量は必ず0で、上記のOP_RETURN出力フォーマットに準拠している必要がある。
nonce
がリクエストで送信したものと同じかチェックする。- シーケンス番号が0であること以外は、PoPの入力が全てトランザクションTと全く同じであることを確認する(署名は除外)。この時入力の順序もTと同じこと。
- 全入力のスクリプトを実行し、全てtrueを返すこと。
- PoP出力に書かれている
txid
が、実際に証明対象のトランザクションであるかチェックする。もし、どのトランザクションか知らない場合は、実際の製品やサービスに支払われたトランザクションを確認すること。 - 上記全てパスしたら
valid
を返す。
セキュリティの考慮事項
- 誰かがPoPリクエストをインターセプトし、その中のパラメータを変更する可能性があるが、これはSSLなどの安全な接続を使うことで軽減できる。以下のような改竄が考えられる↓
- PoPの送付先を変更し、PoPを盗む
- labelを変更し、意図しないPoPに署名したり、ウォレットに記録がないラベルが設定されるとサービスが利用できなくなる。
nonce
を変更することで、PoPはサーバー上の検証で失敗する。
- PoPリクエストを改竄してPoPを盗み、
nonce
が一致するまでサーバにリクエストを送る。実際に合致する確率は1 / 248。サーバーにはこの種の総当り攻撃を検出する仕組みを用意するか、少なくともPoPリクエストを100ms遅らせるなどしてプロセスを遅くする。 - ウォレットに資金がない場合でも、PoP生成器としての価値がある可能性がある。そのためウォレットの残高が0でもセキュリティを維持することは重要。
- トランザクションのmalleabilityによりサーバーとクライアントで異なる
txid
を認識している可能性がある。その場合、クライアントはサーバーに対しトランザクションの証明ができない。ウォレットは自身がブロードキャストした時のtxid
に依存してはいけない。代わりにネットワーク上のトランザクションをチェックし、そのトランザクションをリストに加えるなければならない。