bitcoinjs-libでアドレス作成

# はじめに

bitcoinjs-libを使う上で、すぐにアドレス作成できたら便利だと思い書きました。
https://github.com/bitcoinjs/bitcoinjs-lib

# ニーモニックと拡張鍵生成

```getKeys.js
const bitcoin = require('bitcoinjs-lib');
const bip32 = require('bip32');
const bip39 = require('bip39');
const MAINNET = bitcoin.networks.bitcoin;
const TESTNET = bitcoin.networks.testnet;
// let bitcoinNetwork = MAINNET;
let bitcoinNetwork = TESTNET;

function mnemonicToXprivXpub() {
    const mnemonic = bip39.generateMnemonic(256);
    const seed = bip39.mnemonicToSeedSync(mnemonic);
    const node = bip32.fromSeed(seed, bitcoinNetwork);
    const xpriv = node.derivePath("m/44'/1/0").toBase58();
    const xpub = node.derivePath("m/44'/1/0").neutered().toBase58();
    return { mnemonic, xpriv, xpub };
}

const { mnemonic, xpriv, xpub } = mnemonicToXprivXpub();
console.log("mnemonic:");
console.log(mnemonic);
console.log("xpriv:");
console.log(xpriv);
console.log("xpub:");
console.log(xpub);
```

generateMnemonic(256)だと24語出ます。
generateMnemonic()だと12語です。

```
$ node getKeys.js 
mnemonic:
task foster wet panther noble tissue hockey ancient faint mandate suit thank mad wrist where category lecture margin wrap evoke desert topic empty include
xpriv:
tprv8gPHaXkdRLgnE1NgPqZPkH1ffYBiQkuAVowFDi5rGDuxj19vuBsD53cmXHxq8u4gmDv9t8a2YaY56roA8KNZRNTNSe6Yv6jVdDhUvxYbcRj
xpub:
tpubDD5KiwnsZiNT7UQUHVDz9gfnEZhea66557Y2WE89gViMZVQhXagoFYEdhSnsRKtJJrv9yAmEkdCUA68GPmPk9W8tdfq5iWU7JpcXpEUfhyL
```

鍵をプログラム外部のjsonファイルに書いて呼び出すようにします。
拡張秘密鍵と拡張公開鍵は別々のファイルで作成します。

ディレクトリはこれから作成するプログラムと同じです。

```xpriv.json
{
    "xpriv": "tprv8gPHaXkdRLgnE1NgPqZPkH1ffYBiQkuAVowFDi5rGDuxj19vuBsD53cmXHxq8u4gmDv9t8a2YaY56roA8KNZRNTNSe6Yv6jVdDhUvxYbcRj"
}
```

```xpub.json
{
    "xpub": "tpubDD5KiwnsZiNT7UQUHVDz9gfnEZhea66557Y2WE89gViMZVQhXagoFYEdhSnsRKtJJrv9yAmEkdCUA68GPmPk9W8tdfq5iWU7JpcXpEUfhyL"
}
```

拡張公開鍵から公開鍵を得てシングルシグのアドレス3種(P2PKH、P2SH-P2WPKH、P2WPKH)を生成します。


```getSingleSigAddresses.js
const bitcoin = require('bitcoinjs-lib');
const bip32 = require('bip32');
const bip39 = require('bip39');
const { xpub } = require('./xpub.json');
const MAINNET = bitcoin.networks.bitcoin;
const TESTNET = bitcoin.networks.testnet;
// let bitcoinNetwork = MAINNET;
let bitcoinNetwork = TESTNET;

const xpub  = "tpubDD5KiwnsZiNT7UQUHVDz9gfnEZhea66557Y2WE89gViMZVQhXagoFYEdhSnsRKtJJrv9yAmEkdCUA68GPmPk9W8tdfq5iWU7JpcXpEUfhyL";

const pubkeyNode = bitcoin.bip32.fromBase58(xpub, bitcoinNetwork);
const pubkey = pubkeyNode.derive(1).derive(0).derive(0).derive(0).publicKey;

function getP2pkhAddress(){
    const address = bitcoin.payments.p2pkh({ pubkey: pubkey, network: bitcoinNetwork, }).address;
    return address;
}

function getP2shP2wpkhAddress(){
    const address = bitcoin.payments.p2sh({
        redeem: bitcoin.payments.p2wpkh({ pubkey: pubkey, network: bitcoinNetwork, })
    }).address;
    return address;
}

function getP2wpkhAddress(){
    const address = bitcoin.payments.p2wpkh({ pubkey: pubkey, network: bitcoinNetwork, }).address;
    return address;
}

const p2pkhAddress = getP2pkhAddress();
console.log("P2PKH:");
console.log(p2pkhAddress);

const p2shP2wpkhAddress = getP2shP2wpkhAddress();
console.log("P2SH-P2WPKH:");
console.log(p2shP2wpkhAddress);

const p2wpkhAddress = getP2wpkhAddress();
console.log("P2WPKH:");
console.log(p2wpkhAddress);

```

```
$ node getSingleSigAddresses.js 
P2PKH:
mhJeuuUmHezU6Z4311WGZLVZMV2GTWcPGw
P2SH-P2WPKH:
2N7XBZfKVZ3h9ysFyZLroQpQ5cwxzY25tWE
P2WPKH:
tb1qzwwvqxr2m8l0kt8trnnhaa48awtq45zauflz5y
```

マルチシグアドレスも作成してみましょう。2-of-3のマルチシグアドレスを作成するためにgetKeys.jsを３回実行して、3つの鍵を得ます。

```
$ node getKeys.js 
mnemonic:
strong resource record gap leave diesel sleep gadget truck bonus omit slice remind sting tuna choice shoulder post turkey connect crane unhappy various rent
xpriv:
tprv8fcb9d93ydh7areMRdpi3sbbAqxwfJhQcis8ESquQhUUWxanngo8DM5yqCzE4TdFw6RmTbLUQZ42qzF8aBZkfnUxQs4qaHZwSw1NVBb24i3
xpub:
tpubDCJdJ3BJ81NnUKg9KHVJTHFhjsUspdtKC2TuWxtCpyGsMSqZR5ciPqhr1KvkWqgnFncdvmjU3VmKiumDnzQjg2YZvniC11PCz3pNcsHwBGY
$ node getKeys.js 
mnemonic:
finish noble first teach visual document chef crash symbol calm spend frost sad edit tray damp chaos sad boost horn garbage neglect mention wrap
xpriv:
tprv8hAX4Q4XfvsMY6SRLFbVkQSmeTNSpBFB9NhWhPoP6t1G6TnMeBLsMYw6vm8zGQhiNL2GtyK9iaomcghRbB2KS1eqCyH6i353XDjTP5UZq7U
xpub:
tpubDDrZCp6mpJZ2RZUDDuG69p6tDUtNyWS5igJHyuqgX9oevx38GaATY3Yy6sSkdUd4GbKtGxoqE6Hn6jU3Svn243WDuQXxAALEGEh1BpGEfYQ
$ node getKeys.js 
mnemonic:
museum room hidden group crew celery pumpkin net school metal frost light census tag replace welcome flock mimic funny diagram glow impose ecology scatter
xpriv:
tprv8gvJrJutiX5jVzgn29o9GbYSutadrUX4hyDS89ZvhE9LdMC53Lr8rrcKejX9zLnYKQKjKeihmM65jGMYdyGxz91HKGVckFZ4QC9DuJQyswn
xpub:
tpubDDcLzix8rtmQPTiZuoTjg1CZUv6a1ohyHGpDQfcE7VwjTqSqfjfj3MEBppwwDxrqAMdsvvaKfx6NzHhyeUsqfwAFbmYEx598Yvmpb5wh1B3
```

マルチシグの秘密鍵は本来は別々の人が持っているので、その想定で、別々に秘密鍵を書いたファイルを作成します。

ディレクトリはこれから作成するプログラムと同じです。

```xpriv1.json
{
    "xpriv": "tprv8fcb9d93ydh7areMRdpi3sbbAqxwfJhQcis8ESquQhUUWxanngo8DM5yqCzE4TdFw6RmTbLUQZ42qzF8aBZkfnUxQs4qaHZwSw1NVBb24i3"
}
```

```xpriv2.json
{
    "xpriv": "tprv8hAX4Q4XfvsMY6SRLFbVkQSmeTNSpBFB9NhWhPoP6t1G6TnMeBLsMYw6vm8zGQhiNL2GtyK9iaomcghRbB2KS1eqCyH6i353XDjTP5UZq7U"
}
```

```xpriv3.json
{
    "xpriv": "tprv8gvJrJutiX5jVzgn29o9GbYSutadrUX4hyDS89ZvhE9LdMC53Lr8rrcKejX9zLnYKQKjKeihmM65jGMYdyGxz91HKGVckFZ4QC9DuJQyswn"
}
```

拡張公開鍵は全て同じファイルに記述します。

```xpubs.json
{
    "xpub1": "tpubDCJdJ3BJ81NnUKg9KHVJTHFhjsUspdtKC2TuWxtCpyGsMSqZR5ciPqhr1KvkWqgnFncdvmjU3VmKiumDnzQjg2YZvniC11PCz3pNcsHwBGY",
    "xpub2": "tpubDDrZCp6mpJZ2RZUDDuG69p6tDUtNyWS5igJHyuqgX9oevx38GaATY3Yy6sSkdUd4GbKtGxoqE6Hn6jU3Svn243WDuQXxAALEGEh1BpGEfYQ",
    "xpub3": "tpubDDcLzix8rtmQPTiZuoTjg1CZUv6a1ohyHGpDQfcE7VwjTqSqfjfj3MEBppwwDxrqAMdsvvaKfx6NzHhyeUsqfwAFbmYEx598Yvmpb5wh1B3"
}
```

xpubs.jsonを読み込んでマルチシグアドレス3種（P2SH、P2SH-P2WSH、P2WSH）を生成します。

```getMultiSigAddresses.js 
const bitcoin = require('bitcoinjs-lib');
const bip32 = require('bip32');
const bip39 = require('bip39');
const { xpub1, xpub2, xpub3 } = require('./xpubs.json');
const MAINNET = bitcoin.networks.bitcoin;
const TESTNET = bitcoin.networks.testnet;
// let bitcoinNetwork = MAINNET;
let bitcoinNetwork = TESTNET;

const pubkeyNode1 = bitcoin.bip32.fromBase58(xpub1, bitcoinNetwork);
const pubkey1 = pubkeyNode1.derive(1).derive(0).derive(0).derive(0).publicKey;

const pubkeyNode2 = bitcoin.bip32.fromBase58(xpub2, bitcoinNetwork);
const pubkey2 = pubkeyNode2.derive(1).derive(0).derive(0).derive(0).publicKey;

const pubkeyNode3 = bitcoin.bip32.fromBase58(xpub3, bitcoinNetwork);
const pubkey3 = pubkeyNode3.derive(1).derive(0).derive(0).derive(0).publicKey;

const pubkeys = [
    pubkey1,
    pubkey2,
    pubkey3,
].map(Buffer => Buffer);

function getP2shAddress(){
    const address = bitcoin.payments.p2sh({
        redeem: bitcoin.payments.p2ms({ m: 2, pubkeys, network: bitcoinNetwork, }),
    }).address;
    return address;
}

function getP2shP2wshAddress(){
    const address = bitcoin.payments.p2sh({
        redeem: bitcoin.payments.p2wsh({
            redeem: bitcoin.payments.p2ms({ m: 2, pubkeys, network: bitcoinNetwork, })
        }),
    }).address;
    return address;
}

function getP2wshAddress(){
    const address = bitcoin.payments.p2wsh({
        redeem: bitcoin.payments.p2ms({ m: 2, pubkeys, network: bitcoinNetwork, }),
    }).address;
    return address;
}

const p2shAddress = getP2shAddress();
console.log("P2SH:");
console.log(p2shAddress);

const p2shP2wshAddress = getP2shP2wshAddress();
console.log("P2SH-P2WSH:");
console.log(p2shP2wshAddress);

const p2wshAddress = getP2wshAddress();
console.log("P2WSH:");
console.log(p2wshAddress);
```

```
$ node getMultiSigAddresses.js 
P2SH:
2NGZYty3BtrN9gv4n8y3YLFa1Uos2XffCvR
P2SH-P2WSH:
2N9mgCypNXq8qkx1SHqBkDW4u92rXKsdJjH
P2WSH:
tb1q2adzgyzuy3msvjjfn3pkghf5fnwtwmxcglw3cuk5gvvv3ahmt63qqqa3n6
```

xprivは今回使いませんでしたが、以下のトランザクションに署名する際に使います。

https://qiita.com/shu-kob/items/1a26eb7ec21fcf43cf68

https://qiita.com/shu-kob/items/274f499cba27ab682c8c

https://qiita.com/shu-kob/items/002287e2f95009cb4dee

https://qiita.com/shu-kob/items/d503c2f7352bed3b77b6