btc: bitcoinjs-lib を使う (2)
2025/01/23
はじめに
P2TR を扱うのに node.js で使える bitcoinjs-lib を使ってみよう。
TypeScript 環境
ちょろっと書くには JavaScript の方がチェックされなくて楽なのだが、 トランザクションのデータ構造で変数名を間違えたりすると面倒なので TypeScript にしておこう。
ts-node
だと楽はできるのだが、今回はトランスパイルしてから実行するようにしておこう。
ディレクトリ名を js-keypath
にして、まずは key path の動作を確認する。
$ mkdir js-keypath
$ cd js-keypath
$ npm init -y
$ npm i typescript @types/node
$ npx tsc --init
$ echo -e "node_modules/\ndist/" > .gitignore
$ git commit -a -m "first commit"
トランスパイルしたファイルは別のディレクトリに出力したいので tsconfig.json をちょっとだけ変更。
"outDir": "./dist",
package.json もちょっと変えておこう。
トランスパイルと実行を同時にやるなら ts-node
でいいやんってなるけど、いいんだよ。
"scripts": {
"start": "npx tsc; node dist/index.js"
},
動作確認のためルートディレクトリに index.ts
を作る。
console.log('hello, world');
実行。
$ npm start
> js-keypath@1.0.0 start
> npx tsc; node dist/index.js
hello, world
bitcoinjs-lib 環境
これを書いている時点では bitcoinjs-lib v6.1.7 が rc 無しのバージョンなのでそれを使っておこう。
$ npm i bitcoinjs-lib@v6.1.7 ecpair@v2
※修正(2025/01/24)
このときは単に “ecpair” でインストールしていたが、issue があるためバージョンを指定した。
bitcoind regtest 環境
regtest を立てておく。
P2TR に対応していれば特に bitcoind のバージョンは問わない。
~/.bitcoin/bitcoin.conf
txindex=1
server=1
regtest=1
rpcuser=user
rpcpassword=pass
fallbackfee=0.00001
regtest を一からやり直したい場合は ~/.bitcoin/regtest
を削除する。
ウォレットだけやり直したい場合は ~/.bitcoin/regtest/wallet/test
を削除する。
$ rm -rf ~/.bitcoin/regtest/
$ bitcoind -regtest -daemon
Bitcoin Core starting
$ rm -rf ~/.bitcoin/regtest/wallets/
$ bitcoin-cli -regtest -named createwallet wallet_name=test load_on_startup=true
{
"name": "test"
}
ウォレット “test” にお金を入れよう。
$ addr=`bitcoin-cli -regtest getnewaddress`
$ bitcoin-cli -regtest generatetoaddress 110 $addr
[
...
...
]
$ bitcoin-cli -regtest getbalance
500.00000000
もちろん、テスト用の Bitcoin なのでテストでしか使えません(一応書いておく)。
JSON-RPC アクセスと ERR_REQUIRE_ESM
$ npx tsc --version
Version 5.7.3
$ node --version
v18.20.5
昔作っていた bitcoind と JSON-RPC する関数があったので流用する。
node-fetch
というのを使っていたんだなあ、と適当にインストールしたのだが ERR_REQUIRE_ESM
というエラーが出る。
v2 にするとよいそうだ。
確かに前回はそんなことをした気がする。
node.js は import
ではなく require()
がデフォルトだけど、node-fetch@v3
は import
のみだからエラーになったということかね。
ESM がその違いだけかどうかは知らないけど。
node_modules/
以下のコードは自分でトランスパイルするのではなく、JavaScript 形式になっているものしかないのかな?
そうでなければエラーは出ないだろう。
node.js も拡張子が .mjs
なら ESM 形式で実行するそうだが、npm
のしくみがそうじゃないというかブラウザがそうじゃないということで .mjs
という名前ではないのだろう。
そもそも JavaScript はブラウザで動かす方が主目的だったか。
ならば node.js 側が何とかせねばならぬ。
自分のファイルではないから拡張子を変更するという手段は選ぶことができない。
package.json
に "type": "module"
を変更するとよいそうだ。
が、TypeScript のコマンド tsc
が自分のコードをトランスパイルして node.js で動かせているということは、CommonJS 形式でトランスパイルされているはずである。
ならばそちらも対処がいるだろう。
tsconfig.json
にこれの “ESNext” 以降を指定すればどれでも import
でいけるのかな?
You very likely want “nodenext” for modern Node.js projects and preserve or esnext for code that will be bundled.
と書いてあるので preserve や esnext 辺りが無難とか? preserve は TypeScript 5.4 から追加されているので、それでいいのかな。
指定したら、なんかいろいろ面倒になった・・・。
- import した TypeScript のファイルが見つからない
- 自分で import するファイル名に
.js
を付けなくてはならないらしい(.ts
ではなく)
- 自分で import するファイル名に
- import した JSON でエラー
-
TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module “file:///ほにゃほにゃ/config.json” needs an import attribute of type “json”
- import の最後に
assert { type: 'json' }
とか書かないといかんらしい(stackoverflow)- トランスパイル時にエラーにしてくれればいいのに。。
- その代わりなのか
resolveJsonModule
はtrue
にしなくても使える?
-
一応動いた。
node-fetch
である必要があるのか? という気がしている。
Node v18 から fetch
をサポートしたとか(まだ experimental?)、Node にはそもそも “http” があるとか、”http” は await
で使うのは面倒そうとか。
Twilio さんのブログ経由で見つかった ky というのを使ってみよう。
こちらも動いた。
これが気に入った、というのは思いつかないが、特に fetch
に慣れているわけでもないのでどちらでもよいかな。
おわりに
準備はできたと思う。