hiro99ma blog

何か技術的なこと

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 のバージョンは問わない。

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@v3import のみだからエラーになったということかね。
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 から追加されているので、それでいいのかな。

指定したら、なんかいろいろ面倒になった・・・。

一応動いた。

node-fetch である必要があるのか? という気がしている。
Node v18 から fetch をサポートしたとか(まだ experimental?)、Node にはそもそも “http” があるとか、”http” は await で使うのは面倒そうとか。
Twilio さんのブログ経由で見つかった ky というのを使ってみよう。

こちらも動いた。

これが気に入った、というのは思いつかないが、特に fetch に慣れているわけでもないのでどちらでもよいかな。

おわりに

準備はできたと思う。

< Top page