hiro99ma blog

何か技術的なこと

btc: libwally-core で script path (1)

2025/02/04

はじめに

以前 libwally-core v1.3.1 で key path のサンプルコードを作った。

libsecp256k1 の関数を使って MuSig2 のサンプルコードを作った。

そろそろ script path に取りかかろう。

BIP-340 の見逃し

libwally-core はサンプルにできるものが無いので、まずは計算手順を確認するため 以前自作したコードを確認している。
まだ 2週間くらいなのに記憶が無い。。。

見返していたのだが、署名が learn me a bitcoin と一致しない。
たぶん、乱数を設定しなくしたり SIGHASH_ALL にするのが面倒だったのだろう、と思ったのだがそうでもない。
code を見ると sigMsgsigHash までは一致したのだが、署名計算で tagged hash の “BIP0340/aux”, “BIP0340/nonce”, “BIP0340/challenge” を使っている箇所がある。
bitcoinjs-lib のコードを検索すると確かにある。
名前からすると読んでいなかった BIP-340 のようだ。

どうやら自分で key path や script path を試作したときには完全に見逃していたようだ。
key path や script path に関係なく、単にシュノア署名の関数を通す前に前処理が必要だった。

今回の中で一番面倒な計算がやってきた。。。 何をどうしたらこういう計算が必要だと考えつくのだろうね。

さて、これを自作に実装するか、もうあれは中途半端だから捨ててしまうか。

とかなり悩んだのだが、そういえば libwally-core で key path を実装したときは署名の値も一致していた。
ということは libwally-core では BIP-340 に従ってシュノア署名していることになる。

libscp256k1 のシュノア署名は BIP-340 の計算式

私が呼び出していた wally_ec_sig_from_bytes() は内部で wally_ec_sig_from_bytes_aux() を呼び出している。
(_aux がない場合は引数 aux_randNULL で呼び出したのと同じ扱いになっていた。)
その中からは libsecp256k1 の secp256k1_schnorrsig_sign32()直接呼び出していた
(※ aux_randNULL だとオールゼロと同じ扱いだそうなので wally_ec_sig_from_bytes_aux() を使うようにしよう!!)

libsecp256k1 の関数の中身 を見ると、BIP-340 に出てきた変数名だったり、”bip340” という文字だったり、odd で negate したり “challenge” があったりと、 これはもう BIP-340 に書いてある手順で署名していると考えて良いでしょう。
そうでないと libwally-core で何もしていないのに署名値が一致するはずがない!
やった、実装しなくてよい!!

ここまで BIP に寄り添ってくれるなら secp256k1_fe_is_odd() みたいな奇数・偶数判定する関数も使えると良いのだけどね。
実は知らないだけでそういう関数を使わずに済む上位の関数があったりするのだろうか。

話は元に戻る

libsecp256k1 のシュノア署名は BIP-340 の通りにやってくれることが分かった。
厳密にはまったく同じではなく、最後の verify はやっていない(やるなら自分で呼び出そう)のだが、ささいなことだ。

では、私の実装ではなぜ sigHash まで一致するのに署名があわないのかという問題に戻った。
よくよく調べると、自作の署名関数では internal private key を渡して中で tweak private key を生成して署名していた。
key path だとそれでよいのだがスクリプトの中では tweak は関係ないので internal というか普通に private key で署名する必要があるのだった。

おわりに

自作の P2TR script path を復習して、libsecp256k1 の関数でどの辺を使えばよさそうかはわかったと思う。
あとはそれを頼りに libwally-core の関数を探していけると良いのだが、かなり弱気だ。

< Top page