hiro99ma blog

btc: シュノア署名のトリック

2025/04/28

はじめに

Bitcoinスクリプト命令の OP_CAT のことをしばしば見かける。
Linux の cat と同じく concatenate の略で連結してくれる。
ほとんど標準出力に吐き出すコマンドとしてしか使ってない気がするが、ちゃんと連結するのだ。

そういう記事の中にこちらがあった。

「Schnorr署名とOP_CATを利用したトリックを使うと、スクリプト内でトランザクションデータにアクセス(正確にはそのハッシュ)できる」とある。 トリック・・・憧れる響きではないか。

libsecp256k1のシュノア署名カスタマイズ

libsecp256k1 v0.6.0 を使う。

通常というか、P2TR の署名をする場合には secp256k1_schnorrsig_sign32() を使うのが一般的である。

SECP256K1_API int secp256k1_schnorrsig_sign32(
    const secp256k1_context *ctx,
    unsigned char *sig64,
    const unsigned char *msg32,
    const secp256k1_keypair *keypair,
    const unsigned char *aux_rand32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

この関数を使うと、BIP340Default Signing に沿った計算をしてくれる。 nonce というか rand というかを BIP340のデフォルト式に沿って計算してくれる。

しかしシュノア署名のトリックを使いたい場合、秘密鍵も nonce も 1 にしたい。 そういうちょっと違うパラメータを使いたいときは secp256k1_schnorrsig_sign_custom() を使う。

SECP256K1_API int secp256k1_schnorrsig_sign_custom(
    const secp256k1_context *ctx,
    unsigned char *sig64,
    const unsigned char *msg,
    size_t msglen,
    const secp256k1_keypair *keypair,
    secp256k1_schnorrsig_extraparams *extraparams
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);

extraparams が可変パラメータである。 SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT で初期化してからカスタマイズしたいところを変えていく。 今回は nonce を固定値にしたいので noncefp を上書きした。

通常はハッシュ対象になるのは sigHash なのだが、トリックが作動するのか見たいだけなので適当だ。

$ ./app
G.x =
79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798

m =
79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179879be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179801020304

tagged challenge =
1f9633be270e2686f34d0d00f6f39e88ed8654758db5f149ad02ce0d1a994823

sig =
79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
1f9633be270e2686f34d0d00f6f39e88ed8654758db5f149ad02ce0d1a994824

おお。
ちゃんと “sig” の後半 32 バイトの出力が “tagged challenge” の値+1 になっている。
ちょっと感動した。

Bitcoinスクリプトで使うなら “tagged challenge” には検証すべき VIN に関する BIP341 での sigHash が入るだろう。 デジタル署名なら 64 バイトなのだがこの形式だと “tagged challenge” の 32 バイト分だけが対象になるのでそこそこ計算能力があるなら sigHash はわかるかもしれない。

が、このトリックは署名データから “tagged challenge” というか sigHash というかを出すのが目的ではなく、 最終的な目的は署名から検証しているトランザクションデータの直接見えないデータを参照して 送金先アドレスなどを強制させる「コベナンツ(covenants)」ということをやりたいのだ。

ここに載っていたスクリプトがありがちな感じだったので、 そのコベナンツってのはプラスαくらいに考えていた。

が、コベナンツのシンプルな例として載っていたスクリプトは恐ろしいものだった・・・。
サイズは見てないけど、locking script も unlocking script も 1KB くらいはあるんじゃないのっていうくらい圧倒された。 いや、そんなにはないのだろうけど、気持ちとしてね。


 < Top page


コメント(Google Formへ飛びます)

GitHub

X/Twitter

Homepage