はじめに
bitcoinjs-libを使う上で、すぐにアドレス作成できたら便利だと思い書きました。
https://github.com/bitcoinjs/bitcoinjs-lib
ニーモニックと拡張鍵生成
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": "tprv8gPHaXkdRLgnE1NgPqZPkH1ffYBiQkuAVowFDi5rGDuxj19vuBsD53cmXHxq8u4gmDv9t8a2YaY56roA8KNZRNTNSe6Yv6jVdDhUvxYbcRj"
}
{
"xpub": "tpubDD5KiwnsZiNT7UQUHVDz9gfnEZhea66557Y2WE89gViMZVQhXagoFYEdhSnsRKtJJrv9yAmEkdCUA68GPmPk9W8tdfq5iWU7JpcXpEUfhyL"
}
拡張公開鍵から公開鍵を得てシングルシグのアドレス3種(P2PKH、P2SH-P2WPKH、P2WPKH)を生成します。
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回実行して、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
マルチシグの秘密鍵は本来は別々の人が持っているので、その想定で、別々に秘密鍵を書いたファイルを作成します。
ディレクトリはこれから作成するプログラムと同じです。
{
"xpriv": "tprv8fcb9d93ydh7areMRdpi3sbbAqxwfJhQcis8ESquQhUUWxanngo8DM5yqCzE4TdFw6RmTbLUQZ42qzF8aBZkfnUxQs4qaHZwSw1NVBb24i3"
}
{
"xpriv": "tprv8hAX4Q4XfvsMY6SRLFbVkQSmeTNSpBFB9NhWhPoP6t1G6TnMeBLsMYw6vm8zGQhiNL2GtyK9iaomcghRbB2KS1eqCyH6i353XDjTP5UZq7U"
}
{
"xpriv": "tprv8gvJrJutiX5jVzgn29o9GbYSutadrUX4hyDS89ZvhE9LdMC53Lr8rrcKejX9zLnYKQKjKeihmM65jGMYdyGxz91HKGVckFZ4QC9DuJQyswn"
}
拡張公開鍵は全て同じファイルに記述します。
{
"xpub1": "tpubDCJdJ3BJ81NnUKg9KHVJTHFhjsUspdtKC2TuWxtCpyGsMSqZR5ciPqhr1KvkWqgnFncdvmjU3VmKiumDnzQjg2YZvniC11PCz3pNcsHwBGY",
"xpub2": "tpubDDrZCp6mpJZ2RZUDDuG69p6tDUtNyWS5igJHyuqgX9oevx38GaATY3Yy6sSkdUd4GbKtGxoqE6Hn6jU3Svn243WDuQXxAALEGEh1BpGEfYQ",
"xpub3": "tpubDDcLzix8rtmQPTiZuoTjg1CZUv6a1ohyHGpDQfcE7VwjTqSqfjfj3MEBppwwDxrqAMdsvvaKfx6NzHhyeUsqfwAFbmYEx598Yvmpb5wh1B3"
}
xpubs.jsonを読み込んでマルチシグアドレス3種(P2SH、P2SH-P2WSH、P2WSH)を生成します。
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は今回使いませんでしたが、以下のトランザクションに署名する際に使います。