btc: libwally-core を使う (2)
2025/01/27
はじめに
C言語で libwally-core を使って P2TR key path のトランザクションを作る。
- ElementsProject/libwally-core: Useful primitives for wallets
- libwally-core documentation — libwally-core 1.3.1 documentation
当てを探す
P2TR key path のアドレスを作るのは難しくなかった。
計算に必要な要素が明確だったからだ。
しかしトランザクションを作って署名するとなると複数のデータ構造が関わるし、 そのライブラリがどういうやり方を望んでいるかを理解しないとひどく面倒になる。
例えば前回の bitcoinjs-lib は BIP32 を使うと自作する部分がかなり少ないが、 BIP32 を使わずに秘密鍵を持ってくると途端に実装する部分が増える、などだ。
ただ libwally-core はサンプルが分かりづらい!と私は思う。
テストコードはあるのだが、ctest はきつい。
PSBT は PSBT のデータを出力する定義通りの PSBT で、部分的に署名したいという表面的な意味ではないのかな。
こういうよくわからないときは AI 氏に訊いてみる。
そのために ChatGPT と GitHub Copilot Free がいるのだ。
libwally-coreでP2TR key pathのトランザクションに署名するC言語のサンプル
どちらも生成してくれたが、どちらも同じくらいダメだった。
ダメさでいえば ChatGPT の方が強いかな。
お金も払わずに期待しすぎだ、私。
ただ、呼び出す API の選別には使えるんじゃないかと期待している。
見比べよう。
- ChatGPT
- wally_bip32_key_from_seed
- wally_ec_public_key_from_private_key
- wally_addr_segwit_from_bytes
- wally_tx_init_alloc
- wally_tx_input_init_alloc
- wally_tx_output_init_alloc
- wally_tx_get_bip341_sighash
- wally_ec_sig_from_bytes
- wally_tx_set_input_script
- wally_tx_to_bytes
- GitHub Copilot
- wally_ec_private_key_verify
- wally_ec_public_key_from_private_key
- wally_addr_segwit_from_bytes
- wally_tx_init_alloc
- wally_tx_add_input
- wally_script_push_from_bytes
- wally_tx_add_output
- wally_tx_sign
wally_addr_segwit_from_bytes()
までは流れが違うにしてもだいたい同じ。
しかし署名のところから結構違う。
ChatGPT は wally_ec_sig_from_bytes()
で ECDSA の署名をしているよう。
Copilot は wally_tx_sign()
で署名しようとしている。
そう書くと Copilot の方がよさそうに見えるが wally_tx_sign()
は定義が無い。。。
うーん、ドロー!
ただ、ECDSA 用かもしれないが関数の候補が出てきただけでもありがたい。
key path spent
いろいろ手順を踏みながら libwally-core の API を使っていくことで何とかテストデータと一致した。
トランザクションの構造体 wally_tx
にデータを詰め、最後に wally_tx_to_bytes()
でトランザクションデータを出力して実際のデータと一致するかを確認していった。
wally_tx_from_bytes()
で生データからwally_tx
に変換wally_tx_init_alloc()
で初期化し、既存のデータをwally_tx_add_input()
とwally_tx_add_output()
で設定wally_tx_init_alloc()
で初期化し、自分で sigHash を作って署名する
最後のパターンでどの API を使えばよいのかがなかなかわからなかった。
“keypath” が入った API がいくつかあるのだが、これは PSBT の API 用なのか今回は使えなかった。
PSBT でやっていけばよかったのかもしれないが、それはそれで API の使い方が分からないのだ。
script path に対応するなら PSBT の方が自然かもしれないので、今後の検討としよう。
メモリの解放関係がちょっと自信ない。
同じような API でも _init
と _alloc
がある場合、_init
は変数そのものは定義しておいて関数呼び出しで初期化、_alloc
は変数はポインタにしておいて関数呼び出しでメモリの確保と初期化を行う、というパターンのようだ。
ダブルポインタになっていたら alloc 系だと思っておいてよさそう。
free する関数があると思うので、最後はそれを呼び出す。
たぶん wally_cleanup()
すると free し忘れたものは全部解放してくれるんじゃないかと思うが調べてはいない。
おわりに
手探り過ぎて自信がないのだが、計算は合っているので方向はそこまで間違えてないだろう。
署名が 1つしかないのでインデックス値の使い方で間違いはあるかもしれない(全部ゼロだったので)。
script path に取りかかるにはまだ慣れが足りないので、ほどほどなサンプルコードを探したい。