btc: Output Descriptors
2025/02/24
はじめに
Bitcoin のウォレットを作るとき、だいたい 12単語や 24単語のニモニックをランダムに決め、そこから seed を計算して秘密鍵を世代ごとに規則に従って作ることができるようにするのが一般的だ。
鍵から直接アドレスを作る場合はそれで良いが、Bitcoin にはスクリプトもある。
その場合はどうしようもないので各自で保存していた。
そこに登場したのが Output Descriptors である。
・・・と特に調べもせず長いことそう思っていたのだが、そろそろちゃんと調べよう。
- BIP-380
- descriptors
- Output Script Descriptorの基本仕様を定義したBIP-380 - Develop with pleasure!
- Descriptor wallet で Multisig
なんなのか
Output Descriptors は output script を表現するための言語だ。
This is a simple language which can be used to describe collections of output scripts.
略すときは “descriptor(s)” であることがほとんどだと思う。
Output Descriptors に対応したウォレットを descriptor wallet と呼ぶなどだ。
Bitcoin Core のビルドでも “SQLite is required for the descriptor wallet” などと書いてある。
該当する BIP は BIP-380 で、こちらは “Output Script Descriptors” だ。
ただ途中は “descriptors” だけだったりするので、そういうものと思っておけばよさそうだ。
descriptors ではこれまでのウォレットを old wallet と呼んでいるが、 Bitcoin Core のビルドで descriptor wallet は別扱いになっているし、鍵もスクリプトも両方管理できるかもしれないがスクリプト専用になるんじゃないかな?
いかんいかん、まだ中身を読んでいないので勝手な推測をしてしまった。
なにが表現できるのか
詳細を知りたいわけではないので、何ができるのかを知りたい。
output script とはつまり vout の scriptPubKey だろうか。
といってもウォレットなんだし、秘密鍵や seed 的なものも扱えるはずだ。
P2TR の例を見てみよう。
- bips/bip-0386.mediawiki at master · bitcoin/bips
tr()
を使うtr(KEY)
: script path 無しtr(KEY, TREE)
KEY
は 32byte の internal public key / WIF private key / xprv / xpub などtr()
の結果は x-only の tweaked pubkey を HEX文字列にしたもの(64文字)- と書いてあるが Test Vectors は witness program になってる。あえてか?
Test Vectors の tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)
だけ確認したが、確かに internal pubkey だった。
第2引数に pk()
があるのは、これが TREE
というやつだろう。
この pk()
は BIP-381 のと同じなんだろうか?
pk(KEY) = <KEY> OP_CHECKSIG
ということらしいが、これは単にそういうスクリプトの例なのだろう。
tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)
は WIF での privkey なんだろうか?
試した。
WIF デコードして internal private key として計算すると一致した。
おわりに
xprv や xpub も使えるので、BIP-32 を置き換えるものではない。
やはり、HDウォレットは今まで通りで、独自でやっていたスクリプトのインポート/エクスポートのフォーマットが共通になったと思っておけば良いかな。
ニモニックからウォレットを復元すると、HD ウォレットの 2層目が何かを指定しないと復元できないことがある。
モバイルウォレットはもしかしたら指定無し(そのウォレットで固定)かもしれないが、m/44'
だとか m/84'
だとかを指示されないと UTXO を探しに行けないからだ。
Descriptors だと tr()
とか wpkh()
とかを付けるのでその辺は間違いがなさそうだが、まあニモニックで復元するタイプの方が多いよねぇ。
ニモニックにせよ xprv にせよどっちにしても記憶できるものじゃない(少なくとも私は無理)。
既にあるメタルなウォレットは単語の頭4文字分くらいで 24単語まで対応したものしか無いだろう。
長期で運用しつつ、倉庫に入れたりできるような形式ってのは難しいですな。
そもそもなんで私が調べようと思ったかというと、Rust の勉強に飽きてきたので BDK という Rust のサンプルを動かそうと思ったら、最初に出てきたのが Descriptor Wallet だったからだ。
そのままやってもよかったんだけど、少しくらい分かってからやらないと面白くないしね。