Develop with pleasure!

福岡でCloudとかBlockchainとか。

P2SHでネストしたP2WPKHベースのアカウント導出スキームを定義したBIP-49

HDウォレットのマルチアカウント機能がBIP-44で定義されているけど↓

techmedia-think.hatenablog.com

segwitがアクティベートされたこともあり、これにP2SHでネストされたP2WPKHをサポートしようということでBIP-49が新たに追加されたので見てみる↓

https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki

BIPの更新に伴い2019/12/18時点の内容で以下も更新

動機

同じマスターシードもしくは単一アカウントを持つ異なるHDウォレット間でシームレスにコインが利用できるように、BIP-141で定義されているP2SHでネストしたP2WPKHを使用するトランザクションでも、共通の鍵導出スキームが必要になる。

ユーザーはsegregated witness専用のアカウントを作る必要があり、またそのアカウントを検出し適切に処理することができるのはこのBIPと互換性のあるウォレットのみである。

検討事項

現行のBIP-44に対応したウォレットでは、以下の2つのアプローチが可能。

  1. ユーザーが既に使用しているアカウントで利用可能にし、そこにsegregated witnessでエンコードされたアドレスを追加する。
    1. BIP-44で定義されているものと同じ公開鍵を使用し、通常のP2PKHのアドレスに加えてP2SHアドレスも導出する。
    2. 同じアカウントルートは使用するが、そこからは外部チェーンと内部チェーンのルートを分岐し、そこでsegregated witness専用の公開鍵を生成する。
  2. segregated witnessのアドレス専用のアカウントを作成する。

1の解決策には共通の問題がある。ユーザーがこのBIPと互換性のないウォレットにマスターシードをインポートするとアカウントは表示されるが、一部のUTXO(P2SHでネストしたP2WPKHのUTXO)が表示されないといったことが起きる可能性がある。

そのためこのBIPでは、アカウントが表示されるかされないかで判断ができる、2の解決策を採用する。ユーザーは別のウォレットで同じシードをインポートして残高を確認する必要はない。

仕様

このBIPでは、BIP-32のルートアカウントをベースに、複数の決定性アドレスを導出するための2つのステップを定義する。

公開鍵の導出

ルートアカウントから公開鍵を導出するために、このBIPではBIP-44で定義されているアカウント構造を使用するが、トランザクションのシリアライゼーションフォーマットが異なることを表すため、レベルpurposeの値を変える。

m / purpose' / coin_type' / account' / change / address_index

Pathレベルpurposeの値には49を使用する。他のPathレベルの値はBIP-44の定義のまま。

アドレスの導出

導出された公開鍵からP2SHアドレスを導出するため、BIP-141で定義されている以下のルールを使用する。

witness:      <署名> <公開鍵>
scriptSig:    <0 <20バイトの公開鍵ハッシュ>>
              (0x160014{20バイトの公開鍵ハッシュ})
scriptPubKey: HASH160 <20バイトのスクリプトハッシュ> EQUAL
              (0xA914{20バイトのスクリプトハッシュ}87)

拡張鍵のバージョン

拡張鍵をシリアライズする場合、このスキームは代替のversion byteを使用する。拡張公開鍵は0x049d7cb2を使ってypubプレフィックスを生成し、拡張秘密鍵0x049d7878を使ってyprvプレフィックスを生成する。Testnetは0x044a5262を使ってupubを、0x044a4e28を使ってuprvを生成する。

追加で登録されているversion byteはSLIP-0132にリストアップされている。

後方互換

このBIPは↑の検討事項に記載したように後方互換性がない。互換性のないウォレットではアカウントが見つけられず、ユーザーは何かが間違っていることに気づくだろう。

Test vectors

masterseedWords = abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
masterseed = uprv8tXDerPXZ1QsVNjUJWTurs9kA1KGfKUAts74GCkcXtU8GwnH33GDRbNJpEqTvipfCyycARtQJhmdfWf8oKt41X9LL1zeD2pLsWmxEk3VAwd (testnet)

// Account 0のルート秘密鍵 = m/49'/1'/0'
account0Xpriv = uprv91G7gZkzehuMVxDJTYE6tLivdF8e4rvzSu1LFfKw3b2Qx1Aj8vpoFnHdfUZ3hmi9jsvPifmZ24RTN2KhwB8BfMLTVqaBReibyaFFcTP1s9n (testnet)
account0Xpub = upub5EFU65HtV5TeiSHmZZm7FUffBGy8UKeqp7vw43jYbvZPpoVsgU93oac7Wk3u6moKegAEWtGNF8DehrnHtv21XXEMYRUocHqguyjknFHYfgY (testnet)

// Account 0の最初の受信用秘密鍵 = m/49'/1'/0'/0/0
account0recvPrivateKey = cULrpoZGXiuC19Uhvykx7NugygA3k86b3hmdCeyvHYQZSxojGyXJ
account0recvPrivateKeyHex = 0xc9bdb49cfbaedca21c4b1f3a7803c34636b1d7dc55a717132443fc3f4c5867e8
account0recvPublickKeyHex = 0x03a1af804ac108a8a51782198c2d034b28bf90c8803f5a53f76276fa69a4eae77f

// アドレスの導出
keyhash = HASH160(account0recvPublickKeyHex) = 0x38971f73930f6c141d977ac4fd4a727c854935b3
scriptSig = <0 <keyhash>> = 0x001438971f73930f6c141d977ac4fd4a727c854935b3
addressBytes = HASH160(scriptSig) = 0x336caa13e08b96080a32b5d818d59b4ab3b36742

// base58checkエンコードしたtestnetのアドレス
address = base58check(prefix | addressBytes) = 2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2 (testnet)

所感

  • 単純にSegwit用のアドレス導出すれば良いんじゃ?と思ってたけど、ウォレットの互換性を意識しないといけないのね。
  • 今回のBIPの対象はP2SHでネストしたP2WPKHが対象なので、Bech32ベースのネイティブなP2WPKHの導出スキームはまた別途BIPで定義するのかね?
  • Bech32でP2WPKHのアドレスフォーマットが策定されたとはいえ、まずはそれをどうHDウォレットで管理するか仕様決めないと普段使ってるウォレットの対応も進まないよね。