Bitcoinのウォレットの多くは、1つのマスターシードから各秘密鍵やアドレスの導出を行う階層的決定性ウォレットをサポートしている。この仕組みを使うことで、シードさえ覚えていれば、ウォレットを復元することができる。さらに、ウォレットが複数のアカウントを管理できるように、この各階層の使用方法を定義したのがBIP-44。
そして、Segwitがアクティベートされると、P2SH-P2WPKHや、P2WPKH用のアドレスを導出するためにBIP-44を拡張したBIP-49やBIP-88が定義された。
↑はいずれも単独の署名を必要とする(シングルシグの)ウォレット用の仕様で、マルチシグ用の導出仕様としてBIP-45が定義されている。
ただ、これらのアドレス仕様はスクリプトタイプ(P2PKH、P2SH、P2WPKH、P2SH-P2WPKHなど)を固定したもので、スクリプトタイプが増えると、その分新しいBIPが増えていくという状況になっている。
使用するスクリプトタイプについては、最近は↓のようなdescriptorやdescriptor templateを使って記述する方向になりつつあり、
# 2つの拡張公開鍵を使った1-of-2のP2WSHマルチシグアウトプットのdescriptor wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/0/*))
マルチシグの鍵のソートをBIP-45の導出パスに含めるような方法と組み合わせると情報が冗長になる。そのため最近の方向性であるdescriptorやPSBTの仕様と合わせて利用できるように新しくマルチシグ用の導出パスを定義したのがBIP-87↓
https://github.com/bitcoin/bips/blob/master/bip-0087.mediawiki
といっても仕様を見ると、パス仕様は基本的にBIP-44と同じで、purpose
がBIP番号によって変わるだけ。まぁマルチシグ用の鍵導出であることを明示的に宣言したもので、使用するスクリプトタイプなどはdescriptor側で処理してねという切り分け。そのため、ウォレットは基本的にdescriptorを保存する必要がある。それらを利用してマルチシグのセットアップする仕様が、こないだ記事に書いたBIP-129のBSMS↓
techmedia-think.hatenablog.com
※ 以下、BIP-87の訳
概要
このBIPは、BIP-0032(以降BIP32)に記載されているアルゴリズムおよび、BIP-0044(以降BIP44)に記載されているマルチアカウント階層に基づいて、決定性マルチシグウォレットの階層を定義する。
このBIPは、BIP43の特定のアプリケーションである。
Copyright
このBIPのライセンスは2-clause BSD license。
動機
よりユーザーフレンドリーな(オフラインの)マルチシグウォレットの増加や、descriptor言語やBIP-0174 (Partially Signed Bitcoin Transactions)などの新しい技術の採用に伴って、すべての新技術を利用した共通の導出スキームを作る必要がある。
背景として、BIP 44/49/84は、以下のように定義されている:
m / purpose' / coin_type' / account' / change / address_index
ここでBIP43のpurpose'
パスは、各スクリプト(P2PKH、P2WPKH-in-P2SH、P2WPKH)毎に分かれている。シングルシグ用のウォレットでは、導出毎にスクリプトを用意することで、秘密鍵の情報だけで簡単にバックアップと復元ができる。
マルチシグウォレットでは、バックアップと復元に必要な情報が増える(すべての共同署名者の公開鍵など)。これらのスクリプト毎の導出は、その情報を提供するdescriptorによって冗長になる(同様にアウトプットスクリプトのコレクションも指定する)。マルチシグの導出パスには最新の標準化が必要だ。現存するものはいくつかあるが、どれも問題がある。例えばBIP45の定義は:
m / purpose' / cosigner_index / change / address_index
で、単一のスクリプトタイプ(ここではP2SH)であることを不必要に要求する。また、各共同署名者の公開鍵をソートするのにcosigner_index
を設定しているが、これも冗長である。descriptorではmulti
を使って公開鍵の順序を設定したり、sortedmulti
を使って(BIP67で定義されている)辞書式の順序でそれらをソートできるからだ。descriptorを作る前に鍵レコードをコーディネーターに送信して、完全な導出パスを作成するために共同署名者間で公開鍵をソートするようなことは、単に不必要な通信ラウンドを追加することになるだけだ。
2つめのマルチシグの標準は、m/48'
で、これは次のように定義される:
m / purpose' / coin_type' / account' / script_type' / change / address_index
BIP 44/49/84のパスに従いスクリプト毎に個別のBIPを持つのではなく、ベンダーは導出パスにscript_type'
を挿入することを決めた( P2SH-P2WSH=1、P2WSH=2、Future_Script=3など)。前述したように、descriptorがスクリプトを設定するため、これは不要だ。スクリプト毎に新しいBIPを用意する条件を取り除くことで保守作業を削減しようとしているが、この場合もscript_type
のリストを保守する必要がある。
このホワイトペーパーの後半で提案する構造は、これらの問題を解決し、非常に包括的だ。これによりスクリプトタイプに関係なく、マルチパーティ、マルチシグ、階層的決定性ウォレットにおいて、複数のアカウントやアカウント毎の外部/内部チェーン、チェーン毎に何百万のアドレスを扱うことができる*1。
このペーパーは、BIP44からインスパイアされたもの。
仕様
鍵のソート
descriptorをサポートするウォレットは、BIP67にいよる決定論的な鍵のソートを(sortedmulti
関数によって)本来サポートしているため、決定論的にソートされた公開鍵から可能なマルチシグのアドレス/スクリプトを導出することができる。
パス階層
鍵とスクリプトを同じレイヤーで混在させるべきではない。ウォレットは、スクリプトタイプとは無関係に拡張秘密鍵/拡張公開鍵を作成すべきで、一方でdescriptor言語はウォレットに指定された公開鍵でマルチシグアウトプットを監視できるように指示する。
BIP32のパスには5つの階層を定義する:
m / purpose' / coin_type' / account' / change / address_index
パス内のh
および'
はBIP32の強化導出が使用されることを示す。
それぞれの階層には特別な意味があり、以下の章で説明する。
Purpose
Purposeは、BIP43に従って定数87'
を設定する。これは、このノードのサブツリーがこの仕様に従って使われることを示す。この階層では強化導出が使われる。
Coin type
1つのマスターノード(シード)を複数のBitcoinネットワークで使用できる。さまざまなネットワークで同じすケースを共有するといくつかの欠点がある。
この階層では、ネットワーク毎に個別のサブツリーが作成され、ネットワーク全体でアドレスの再利用が回避され、プライバシーの課題が改善する。
Coin type 0
がmainnetで、1
はテストネット(testnet、regtest、signet)。
この階層では強化導出が使われる。
Account
この階層は、BIP44のパターンに従って鍵スペースを個別のユーザーIDに分割するため、ウォレットが異なるアカウント間でコインを混合することはない。
ユーザーはこれらのアカウントを使って、銀行口座と同じ方法で資金を整理できる。(すべてのアドレスが公開される)寄付用や、貯蓄、共用など。
アカウントは、インデックス0
から順番に昇順で番号が振られる。この番号はBIP32導出仕様の子インデックスとして使われる。
この階層では強化導出が使われる。
新しいウォレットが参加するたび、もしくは秘密鍵/公開鍵が作成されるたびに、プライバシー保護と暗号処理の両方の目的でこの階層をインクリメントするのが重要だ。例えば新しい鍵レコードをコーディネーターに送信する前に、ウォレットはアカウントの階層をインクリメントする必要がある。これにより、ECDSA署名およびSchnorr署名、異なるスクリプトタイプ、同じウォレットタイプ間での鍵の再利用を防止される。
Change
定数0
は外部チェーンに使用され、定数1
は内部チェーン(お釣り用のアドレス)に使用される。外部チェーンは、ウォレットの外部から見えることを意図したアドレスに使われる(支払いの受け取りなど)。内部チェーンはウォレットの外部に見えることお意図していないアドレスに使用され、トランザクションでお釣りを戻す際に使われる。
この階層では、公開導出が使用される。
Index
アドレスはインデックス0
から順に昇順に番号が振られる。この番号はBIP32導出仕様の子インデックスとして使われる。
この階層では、公開導出が使用される。
アドレスの検出
アドレスの生成と検出には、共同署名者の結合された鍵レコードから生成されるmultisig descriptorもしくはdescriptor templateを使用する必要がある。
descripto templateについてはBIP-0129 (Bitcoin Secure Multisig Setup) 参照。descriptorもしくはdescriptor templateには、BIP-0174との互換性を最大限高めるため、key origin informationを含めるべきである。
例えば:
次のようなdescriptor templateと導出パスの制約は:
wsh(sortedmulti(2,[xfpForA/87'/0'/0']XpubA/**,[xfpForB/87'/0'/0']XpubB/**))
/0/*,/1/*
次の2つの具体的なdescriptorに分解される:
wsh(sortedmulti(2,[xfpForA/87'/0'/0']XpubA/0/*,[xfpForB/87'/0'/0']XpubB/0/*)) wsh(sortedmulti(2,[xfpForA/87'/0'/0']XpubA/1/*,[xfpForB/87'/0'/0']XpubB/1/*))
アドレスを検出するためには、受信とお釣りの両方のdescriptorをインポートし、以下に記載するgap limitを遵守する。
アドレスのgap limit
アドレスのgap limitは現在20に設定されている。ソフトウェアが連続して20の未使用アドレスに達すると、それ以降の未使用のアドレスはないと想定し、アドレスチェーンの検索を停止する。
ウォレットソフトウェアは、ユーザーが複数の未使用アドレスを生成して外部descriptorのgap limitを越えようとすると警告する必要がある。
後方互換性
descriptor(および特定のウォレット実装)でサポートされているスクリプトはすべて、このBIPと互換性がある。
このBIPに準拠するウォレットは、descriptorウォレットであるため、後から適切に復元するためには、共同署名者が自分の秘密鍵の情報とdescriptorをバックアップする必要がある。これはユーザーの負担にはならないはずである。というのも、M of Nのマルチシグの復元操作では、M個のシードに加えてすべての協同署名者の公開鍵を提供する必要があり、descriptorは、key origin informationを含むこの情報を標準化された形式で提供し、エラー検出も行う。
サンプル
ネットワーク | アカウント | チェーン | アドレス | パス |
---|---|---|---|---|
mainnet | first | external | first | m / 87' / 0' / 0' / 0 / 0 |
mainnet | first | external | second | m / 87' / 0' / 0' / 0 / 1 |
mainnet | first | change | first | m / 87' / 0' / 0' / 1 / 0 |
mainnet | first | change | second | m / 87' / 0' / 0' / 1 / 1 |
mainnet | second | external | first | m / 87' / 0' / 1' / 0 / 0 |
mainnet | second | external | second | m / 87' / 0' / 1' / 0 / 1 |
testnet | first | external | first | m / 87' / 1' / 0' / 0 / 0 |
testnet | first | external | second | m / 87' / 1' / 0' / 0 / 1 |
testnet | first | change | first | m / 87' / 1' / 0' / 1 / 0 |
testnet | first | change | second | m / 87' / 1' / 0' / 1 / 1 |
testnet | second | external | first | m / 87' / 1' / 1' / 0 / 0 |
testnet | second | external | second | m / 87' / 1' / 1' / 0 / 1 |
testnet | second | change | first | m / 87' / 1' / 1' / 1 / 0 |
testnet | second | change | second | m / 87' / 1' / 1' / 1 / 1 |
*1:なぜマルチシグウォレットだけにこの構造を提案するのか? 現在、シングルシグのウォレットは、(通常、BIP39フォーマットの)マスター秘密鍵データのみを使って資金を復元することができる。ユーザーが使用した導出を覚えていなくても、ウォレットの実装は一般的なスキーム(BIP44/49/84)を反復できる。この提案された階層では、ユーザーは追加のデータ(descriptor)をバックアップするか、ウォレットが復元時にすべてのAccount階層のすべてのスクリプトタイプを試行する必要がある。このため、descriptor言語がスクリプトタイプと同様に署名タイプを処理するが、このスクリプトに依存しない階層は、マルチシグウォレットのみに制限するのが最適だ。