hiro99ma blog

何か技術的なこと

btc: MuSig

2025/01/31

はじめに

P2TR の勉強をしている。
最近は実装寄りのところだけやっていたが、そういえば複数の公開鍵を使ってアドレスを作り、そのうちのいくつかの署名が無いと使うことができない MultiSig というしくみがあったことを忘れていた。
P2TR では MuSig とか MuSig2 とかいう単語が出てきていたが、どうなっていただろうか。

MultiSig

使い道としては「3人のうち 2人から承認を得ないといけない」というようなときだ。
言葉で表すときは “M-of-N”(N人中 M人)のような形で書き、上記であれば 2-of-3 MultiSig というような呼び方をする。
ちなみに Mastering Bitcoin は m とか n ではわからんということで「t-of-k」という書き方もしていた。 k は “keys” で t は “threshold of required signatures to spend the output” のことらしい。

OP_CHECKMULTISIG という専用の命令もあるのでスクリプトで解く P2SH と同じ扱いでよさそうだが、bitcoind では MULTISIG と別扱いになっている。
経緯はよく知らないが、Mastering Bitcoin の解説によるとここの MULTISIG は純粋(bare)な MultiSig 仕様を指しているとのこと。

enum class TxoutType {
    NONSTANDARD,
    // 'standard' transaction types:
    ANCHOR, //!< anyone can spend script
    PUBKEY,
    PUBKEYHASH,
    SCRIPTHASH,
    MULTISIG,
    NULL_DATA, //!< unspendable OP_RETURN script that carries data
    WITNESS_V0_SCRIPTHASH,
    WITNESS_V0_KEYHASH,
    WITNESS_V1_TAPROOT,
    WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
};

もう少し詳細を調べる

関連する仕様は BIP-11 に書かれている。
2011年とかなり早い段階である。

スクリプトはこうだ。

m {pubkey}...{pubkey} n OP_CHECKMULTISIG

これを解くのに m 個の署名を列挙する。 が、その手前に OP_0 という命令を置く必要がある。

OP_0 ...signatures...

この OP_0 はもうおまじない的な扱いで、OP_CHECKMULTISIG が持つバグを回避するために使われている。
そしてそのまま仕様に反映されたので OP_0 でないとダメになった。

BIP-11 には n が 3以下と書かれているが、この値の経緯がよくわからない。
書かれている内容からするとこういうことだったのだろう。

そういった経緯から n も上限は 3ということにしたのだと思う。
が、m が 3以下であればいいんじゃなかろうか。

その辺りは Mastering Bitcoin に書かれていた。
BIP-11 の MultiSig 仕様は 3-of-3 が上限だが、P2SH 構造などのスクリプトであれば 15-of-15 まで許容しているとのこと。
IsStandard()関数を見よと書いてあるので確認しよう。

Biggest ‘standard’ txin involving only keys is a 15-of-15 P2SH multisig with compressed keys (remember the MAX_SCRIPT_ELEMENT_SIZE byte limit on redeemScript size). That works out to a (15(33+1))+3=513 byte redeemScript, 513+1+15(73+1)+3=1627 bytes of scriptSig, which we round off to 1650(MAX_STANDARD_SCRIPTSIG_SIZE) bytes for some minor future-proofing. That’s also enough to spend a 20-of-20 CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not considered standard.

キーのみを含む最大の「標準」トランザクションは、圧縮されたキーを持つ 15-of-15 P2SH マルチシグです (redeemScript のサイズに対する MAX_SCRIPT_ELEMENT_SIZE バイト制限に注意してください)。これは、(15(33+1))+3=513 バイトの redeemScript、513+1+15(73+1)+3=1627 バイトの scriptSig に相当し、将来のために 1650(MAX_STANDARD_SCRIPTSIG_SIZE) バイトに切り上げます。これは、20-of-20 CHECKMULTISIG scriptPubKey を使用するのにも十分ですが、このような scriptPubKey は標準とは見なされません。

コメントを信用するなら圧縮された鍵を使った 15-of-15 P2SH MultiSigが最大だそうだ。
BIP-11 の仕様は公開鍵が圧縮されていない場合ということだろうか?
私が Bitcoin を始めたときは既に 33バイトの公開鍵だったためか、今まで MultiSig が P2SH かどうか気にしたことがなかった。

learn me a bitcoin に例となるトランザクションが載っていた。
なるほど、scriptPubKey に公開鍵などがべたっとならんでいたのか。

P2SH の形にすると scriptPubKey にハッシュ値(HASH160: SHA256 した値を RIPEMD160 する)を載せるようになった。
こうすることで scriptPubKey も小さくなるし、スクリプトも少なくともこの時点では見えなくなる。

トランザクションサイズを小さくする仕様が追加されたとする。
ブロックのサイズは固定なので、マイナーはできるだけトランザクションをたくさん詰められるようにしたい(トランザクションの手数料がマイナーの手に入るため)。
トランザクションサイズは手数料に直結するので、送金する人もその仕様を使うようになっていく。
そうやって、だんだんその仕様が主流になっていくのだ。
「ブロックサイズが固定ならそれを増やせばいいじゃないの」という議論もあるが、面倒なので省略だ。

ともかく、今では純粋な MultiSig のトランザクションはほぼ使われていないだろう。
P2SH 形式もほぼないと思う。
P2WSH か P2TR か。

MuSig

m-of-n MultiSig は m個の署名が必要になる。
今までは複数の署名を scriptSig なり witness なりにそのまま載せていた。

まず、BIP-342 によると OP_CHECKMULTISIG 系の命令は OP_RETURN 相当、つまり失敗する命令になっている。
つまり従来形式の MultiSig は P2TR では使えない。

サポートしてくれていれば取りあえずそれでやっておけばよかったのだが、使えないなら仕方ない。
他の命令と比べると複雑なので止めたかったのかな?

別のやり方は? に 4つ紹介されている。

4番目が MuSig2 と呼ばれるものかどうか私には分からなかった。
ちょっと難しい。 MuSig2 は BIP-327 にある。
元の論文とかもあるのだが・・・ちょっと難しい。

script path だったら、その提案は今の bitcoind にされている実装で実現できるか気になるところだが、 key path だったら verify できるかどうかだけの問題だ。
MuSig1 は鍵を集約するから tweak key の作り方が違うだろうし、署名も複数あるから違うはずだ。
よくわからん MuSig2 もそういう感じなんだろう。

実装でそういう情報がたくさんありそうな bitcoinjs-lib を検索した。

libsecp256k1 に MuSig2 の API がマージされている。
Blockstream の libsecp256k1-zkp から持ってきたそうだ。

libwally-core に MUSIG OFF という行があったので、MuSig に関する機能は使わないようにしているのかもしれない。
リポジトリを検索しても issue にほぼ出てこないし。

そう考えると、MuSig は実装はできるものの使うというか運用が難しいということなんだろうか?
いや、そもそも複数の人から署名を集めるという行為自体が難しいな。
PSBT というフォーマットがあるから、まだやりやすいのかもしれないが、それにしても大変だろう。

libsecp256k1 のサンプル

サンプルコードを少しだけ見てみよう。
ドキュメントはこちら

おわりに

MuSig の API があってちょっと試してみようか、くらいの気持ちだったのだが壁は高かった。

< Top page