BIP47 Payment Code を bitcoinrb (Ruby) で実装してみた

# Payment Code
Payment Code は BIP47 で定義されるている Bitcoin のアドレスの形態の一つです。
[bips/bip-0047.mediawiki at master · bitcoin/bips](https://github.com/bitcoin/bips/blob/master/bip-0047.mediawiki)
Bitcoin においてアドレスはプライバシーの観点から再利用することは推奨されていません。
しかし、アドレスを使い回さないということは、同じ人に送金するにもかかわらず毎回銀行口座が変わってしまう状況に近く不便です。
そこで BIP47 では Payment Code と呼ばれるアドレスの別形態を定義することで、同じ Payment Code への支払いをアドレスの再利用を行わずに実現しました。
つまり、UI上同じ Payment Code 宛に複数回の支払いを行っても、Bitcoin プロトコル上では毎回別のアドレスへの送金が行われているのです。
また受け取り側も、複数回の支払いを受けた場合でも送金元ユーザー(Payment Code)を知ることができます。(たぶんそういうことだと思っている。ここ自信ない。)

## 実装例
もっともメジャーな実装は Samourai Wallet の [Paynym](https://samouraiwallet.com/paynym) だと思います。
Java 実装です。

# 環境
[bitcoinrb](https://github.com/chaintope/bitcoinrb) v0.3.1

# Payment Code の仕組み
この記事では言葉で説明しています。
くわしい仕様は BIP を参照してください。
[Gist](#Gist) のコードは [Test Vector](https://gist.github.com/SamouraiDev/6aad669604c5930864bd) を満たすように動くはずです。

## 定義
Payment Code は 基本的には BIP32(HD Wallet) [^1]です。
BIP32 に関連して BIP44[^2] では以下のようなパスの標準形が提案されています。
`m / purpose' / coin_type' / account' / change / address_index`
これに基づいて、Payment Code では `purpose` を `47'`にした BIP32に基づく アドレス郡(なにかもっといい言葉があれば...)から Payment Code を導出します。
Payment Codeの定義は BIP47 の binary serialization に記載されています[^3]。
`Bytes 3 - 34` の `x value` が extended key の x軸の値になります。

### bitcoinrb で実装すると

```sample.rb
bip32_master = Bitcoin::ExtKey.generate_master(seed)
ext_key = bip32_master.derive(47, harden=true).derive(0, harden=true).derive(0, harden=true)

chain_code = ext_key.chain_code

byte3_34 = ext_key.pub.slice(2...ext_key.pub.length) # x of pubkey
byte35_66 = chain_code.unpack('H*').first # chain code

raw_payment_code = '47' + '01' + '00' + '02' + byte3_34 + byte35_66 + ('0' * 26)

payment_code = Bitcoin::Base58.encode(raw_payment_code + Bitcoin.calc_checksum(raw_payment_code))
```

## Notification Transaction
メインの送金を行う前に、送金元が送金先に、送金元の Payment Code を通知する Notification Transaction が必要です。
Notification Transaction を行うことで以下の２点が実現できます。

- Refund
- 支払いのコンテキスト

今回は Refund については触れられません。そこまで実装していないので...。
Payment Code に送られてくる送金は、受信したアドレスによって誰からの送金であるかを知ることができます。
サービスが定期的な支払いを受ける際などに、誰からの支払いが済んでいるかが容易に確認できます。

### Notification Transaction の作り方
Alice から Bob への送金を想定します。
Alice が Bob の Notification Address へ Transaction を作成し、その OP_RETURN に以下の手順で作成するマスクされた Alice の Payment Code を入れます。これが Notification Transaction となります。

### Masked Payment Code の作り方
Alice は Notification Transaction の input となるトランザクションの Alice の秘密鍵と Bob の Notification Address の公開鍵から secp256k1 上の点を導出します。
Alice はその点を元に、Alice の Payment Code をマスクするための blinding factor を計算します。
Payment Code を構成するバイト列のうち `x`を blinding factor の前半 32バイトで、`chain code` を 後半 32バイトでそれぞれ xor を取り、それらを元に Payment Code を導出します。
これがマスクされた Alice の Payment Code です。
blinding factor のもととなる点は ECDH によって Bob と共有されます。したがって Bob は Alice が使用した blinding factor を導出できるため、マスクされた Payment Code から 実際の Alice の Payment Code を得ることができます。


### bitcoinrb で実装すると
```bip47_ext.rb
Alice_Ext = Bitcoin::ExtKey.generate_master(alice_seed)
Alice_Ext_47 = Alice_Ext.derive(47, harden=true).derive(0, harden=true).derive(0, harden=true)

Bob_Ext = Bitcoin::ExtKey.generate_master(bob_seed)
Bob_Ext_47 = Bob_EXT.derive(47, harden=true).derive(0, harden=true).derive(0, harden=true)
```

```sample.rb
# Alice のプライベートキーと Bob 公開鍵から secp256k1 上の点を導出
alice_key = Bitcoin::Key.from_wif('Kx983SRhAZpAhj7Aac1wUXMJ6XZeyJKqCxJJ49dxEbYCT4a1ozRD')
alice_ec = OpenSSL::PKey::EC.new('secp256k1')
alice_ec.private_key = OpenSSL::BN.new(alice_key.priv_key, 16)

bob_pub_bn = OpenSSL::BN.new(Bob_Ext_47.derive(0).pub, 16)

## secp256k1 上の点
secret_point = OpenSSL::PKey::EC::Point.new(alice_ec.group, bob_pub_bn)

## Alice と Bob が共有する Shared secret
common_key = alice_ec.dh_compute_key(alice_secret_point).bth
```



## 送金
Notification Transaction 同様、Alice の秘密鍵と Bob の公開鍵から Bob と共有できる値をつくります。
その値をもとに送金先となる公開鍵を作成します。
Bob の公開鍵を取得する際に、BIP32 におけるパスの値を増加させることで複数回の送金を実行します。


### bitcoinrb で実装すると

```sample.rb
# Alice to Bob Payment
## Alice selects the 0th private key derived from her payment code: 
a = Alice_Ext_47.derive(0).priv
## Alice selects the next unused public key derived from Bob's payment code, starting from zero: 
bobpub = Bob_Ext_47.ext_pubkey.derive(0).pubkey
bob_pub_bn = OpenSSL::BN.new(bobpub, 16)

## Alice calculates a secret point:
alice_ec = OpenSSL::PKey::EC.new('secp256k1')
alice_ec.private_key = OpenSSL::BN.new(a, 16)
alice_secret_point = OpenSSL::PKey::EC::Point.new(alice_ec.group, bob_pub_bn)
secret_point = alice_ec.dh_compute_key(alice_secret_point)

## Alice calculates a scalar shared secret using the x value of secret_point:
## Bob can calcurate this value.
s = Bitcoin::Util.sha256(secret_point).bth

## B + sG
sg = Key.new(priv_key: s)
sg_point = sg.to_point

### B=bob_pub_point
bob_pub_point = Bob.ext_key.derive(0).key.to_point
b_prime = bob_pub_point + sg_point

## 03 が必要だった....
b_prime = '03' + b_prime.x.to_s(16)

key = Key.new(pubkey: b_prime)
key.to_p2pkh == '141fi7TY3h936vRUKh1qfUZr8rSBuYbVBK'
```

## Payment Code におけるコンテキスト

Bob は Alice からの Notification Transaction を受け取ったときから、`b_prime`への送金をモニターします。
`b_prime`は Alice と Bob の間でだけ生成されるアドレスです。 
Carol が Bob に BIP47 での送金を行う場合、送金先アドレスは Carol と Bob の間でだけ生成される別のアドレスになります。
これが、コンテキストです。
Bob は`b_prime`に送られてくる送金はすべて Alice からのものであると知ることができます。

# Gist
クラスにしてほんのちょっとだけきれいにしてみたもの
https://gist.github.com/kannapoix/8207fd1c5b4d69b398bfe98ea5480bb4

# 今後
すべて実装する
PR出したい気持ち...
(いつになることやら)
適宜この記事は update していく予定なので関心ある方はぜひストックを!

# 類似する技術
- Stealth Addresses
- easypaysy: [Bitcoinにアカウント機能を導入するLayer 2プロトコル「easypaysy」 - Develop with pleasure!](https://techmedia-think.hatenablog.com/entry/2019/12/08/115847)

# プライバシー強化する技術はほかにもあるけど、実際 Payment Code ってどうなの
...。
どうなんでしょうか。
要調査。

# 参考
[How BIP47 Reusable Payment Codes enrich Bitcoin and overall cryptocurrency user-experience](https://medium.com/billion-crypto-stories/how-bip47-reusable-payment-codes-enrich-bitcoin-and-overall-cryptocurrency-user-experience-6f929c87a61b)
[bip47 - BIP 47: Bob's first receive address? - Bitcoin Stack Exchange](https://bitcoin.stackexchange.com/questions/84774/bip-47-bobs-first-receive-address)




[^1]: [bips/bip-0032.mediawiki at master · bitcoin/bips](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
[^2]: [bips/bip-0047.mediawiki at master · bitcoin/bips](https://github.com/bitcoin/bips/blob/master/bip-0047.mediawiki)
[^3]: https://github.com/bitcoin/bips/blob/master/bip-0047.mediawiki#binary-serialization