bitcoinjs-libでSegwitシングルシグのトランザクションを作る

# はじめに

https://qiita.com/shu-kob/items/63fa8aafa06cac1c7cda

上記記事で作成したP2WPKHアドレスにSignetで入金し、トランザクションを作成してブロードキャストするところまでをやってみます。

# 準備

## フルノード

https://github.com/kallewoof/signet-platform

Dockerのある環境で、Signetフルノードを立ち上げます。

```
$ ID=$(docker ps -q)
```

```
$ alias bcli="docker exec $ID bitcoin-cli"
```

```
$ bcli getblockchaininfo
{
  "chain": "signet",
  "blocks": 52781,
  "headers": 52781,
  "bestblockhash": "0000011901fe5f02923fd64aa9d1ae97d4defa9e0ee130cd360986f7e896cd57",
  "difficulty": 0.002919030932507782,
  "mediantime": 1629981736,
  "verificationprogress": 0.9999984371265536,
  "initialblockdownload": false,
  "chainwork": "00000000000000000000000000000000000000000000000000000094db3d1966",
  "size_on_disk": 154655081,
  "pruned": false,
  "softforks": {
    "bip34": {
      "type": "buried",
      "active": true,
      "height": 1
    },
    "bip66": {
      "type": "buried",
      "active": true,
      "height": 1
    },
    "bip65": {
      "type": "buried",
      "active": true,
      "height": 1
    },
    "csv": {
      "type": "buried",
      "active": true,
      "height": 1
    },
    "segwit": {
      "type": "buried",
      "active": true,
      "height": 1
    },
    "taproot": {
      "type": "bip9",
      "bip9": {
        "status": "active",
        "start_time": -1,
        "timeout": 9223372036854775807,
        "since": 0,
        "min_activation_height": 0
      },
      "height": 0,
      "active": true
    }
  },
  "warnings": ""
}

```

 "blocks"と"headers"が同じブロック高になれば同期OKです。
同期している間に先に進みます。

## アドレス準備

https://qiita.com/shu-kob/items/63fa8aafa06cac1c7cda

アドレス作成編で作成したP2WPKHを使います。

```
P2WPKH:
tb1qzwwvqxr2m8l0kt8trnnhaa48awtq45zauflz5y
```

## Signet BTC調達

Signet Faucetを使ってP2WPKHに入金します。

https://signet.bc-2.jp/

ときどき、不調な時がありますので、そういう場合は時間をおいてお試しください。

Exploreで確認します。

https://explorer.bc-2.jp/tx/364c422186a0e07e7de4ab71cd29e065920b6c46efce16fb49636cd95dee7e38

## 鍵の準備

https://qiita.com/shu-kob/items/63fa8aafa06cac1c7cda#%E3%83%8B%E3%83%BC%E3%83%A2%E3%83%8B%E3%83%83%E3%82%AF%E3%81%A8%E6%8B%A1%E5%BC%B5%E9%8D%B5%E7%94%9F%E6%88%90

ここで、シングルシグのアドレスを作成しましたが、そのときの拡張秘密鍵、拡張公開鍵を使用します。

```
$ 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
```

拡張秘密鍵と拡張公開鍵は別々のファイルで作成します。

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

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

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


# トランザクション作成

前回のアドレス作成で得たxprivとxprivをセット、
入金時のTXIDとIndexをセットします。

psbt.addOutputの宛先、送金額を適当にセットします。

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

const { xpriv } = require('./xpriv.json');

const privkeyNode = bitcoin.bip32.fromBase58(xpriv, bitcoinNetwork);
const privateKey_wif = privkeyNode.derive(1).derive(0).derive(0).derive(0).toWIF();
console.log("privateKey_wif:");
console.log(privateKey_wif);

function getPubkeyFromXpub(xpub) {
    const pubkeyNode = bitcoin.bip32.fromBase58(xpub, bitcoinNetwork);
    const pubkey = pubkeyNode.derive(1).derive(0).derive(0).derive(0).publicKey;
    return pubkey;
}

const pubkey = getPubkeyFromXpub(xpub);

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

console.log('Witness script:')
console.log(p2wpkh.output.toString('hex'))

console.log('P2WPKH address')
console.log(p2wpkh.address) 

const psbt = new bitcoin.Psbt({ network: bitcoinNetwork });

psbt.addInput({
    hash: '364c422186a0e07e7de4ab71cd29e065920b6c46efce16fb49636cd95dee7e38',
    index: 0,
    sequence: 0xffffffff,

    witnessUtxo: {
    script: Buffer.from(p2wpkh.output.toString('hex'),'hex',),
    value: 100000,
    },
});
psbt.addOutput({
    address: "tb1q2adzgyzuy3msvjjfn3pkghf5fnwtwmxcglw3cuk5gvvv3ahmt63qqqa3n6",
    value: 70000,
});
psbt.addOutput({
    address: "tb1qzwwvqxr2m8l0kt8trnnhaa48awtq45zauflz5y",
    value: 29890,
});

const obj = wif.decode(privateKey_wif);

const privKey = bitcoin.ECPair.fromPrivateKey(obj.privateKey);

psbt.signInput(0, privKey);

psbt.validateSignaturesOfInput(0);
psbt.finalizeAllInputs();
const txHex = psbt.extractTransaction().toHex();

console.log(txHex);
```

```
$ node singleSigTx.js 
privateKey_wif:
cVa9bD3JAoc6hJHfU68PkLWrYmn6AN81PJs7kG6BHR1TeA4nKJtF
02000000000101387eee5dd96c6349fb16ceef466c0b9265e029cd71abe47d7ee0a08621424c360000000000ffffffff027011010000000000220020575a24105c2477064a499c43645d344cdcb76cd847dd1c72d44318c8f6fb5ea2c274000000000000160014139cc0186ad9fefb2ceb1ce77ef6a7eb960ad05d024830450221008b022898583d8dd4eb8136982b3ed7bdd0f632a6613def653d171c1536ef954702206e7d958b881a6e66c42d9ba8c3fd88a9724fc10d9c12a5e15340c564e8a0d3ae0121034c30836747cd7d7a50db1de9ceb1c953a4c308bbf2669c620d62a4370f63b47200000000
```

なお、WIF形式の秘密鍵（privateKey_wif）はBitcoin Coreのウォレットに取り込めます。

```
$ bcli importprivkey cVa9bD3JAoc6hJHfU68PkLWrYmn6AN81PJs7kG6BHR1TeA4nKJtF
```

得られた生トランザクションをデコードして確認します。

```
$ bcli decoderawtransaction 02000000000101387eee5dd96c6349fb16ceef466c0b9265e029cd71abe47d7ee0a08621424c360000000000ffffffff027011010000000000220020575a24105c2477064a499c43645d344cdcb76cd847dd1c72d44318c8f6fb5ea2c274000000000000160014139cc0186ad9fefb2ceb1ce77ef6a7eb960ad05d024830450221008b022898583d8dd4eb8136982b3ed7bdd0f632a6613def653d171c1536ef954702206e7d958b881a6e66c42d9ba8c3fd88a9724fc10d9c12a5e15340c564e8a0d3ae0121034c30836747cd7d7a50db1de9ceb1c953a4c308bbf2669c620d62a4370f63b47200000000
```

フルノードが同期完了した後、ブロードキャストします。

```
$ bcli sendrawtransaction 02000000000101387eee5dd96c6349fb16ceef466c0b9265e029cd71abe47d7ee0a08621424c360000000000ffffffff027011010000000000220020575a24105c2477064a499c43645d344cdcb76cd847dd1c72d44318c8f6fb5ea2c274000000000000160014139cc0186ad9fefb2ceb1ce77ef6a7eb960ad05d024830450221008b022898583d8dd4eb8136982b3ed7bdd0f632a6613def653d171c1536ef954702206e7d958b881a6e66c42d9ba8c3fd88a9724fc10d9c12a5e15340c564e8a0d3ae0121034c30836747cd7d7a50db1de9ceb1c953a4c308bbf2669c620d62a4370f63b47200000000
error code: -26
error message:
min relay fee not met, 110 < 153
```

このエラーは手数料不足で送金できませんでした。

Input - OutPut >= 153 になるように調整します。

```
psbt.addOutput({
    address: "tb1qzwwvqxr2m8l0kt8trnnhaa48awtq45zauflz5y",
    value: 29847,
});
```

```
$ node singleSigTx.js 
privateKey_wif:
cVa9bD3JAoc6hJHfU68PkLWrYmn6AN81PJs7kG6BHR1TeA4nKJtF
02000000000101387eee5dd96c6349fb16ceef466c0b9265e029cd71abe47d7ee0a08621424c360000000000ffffffff027011010000000000220020575a24105c2477064a499c43645d344cdcb76cd847dd1c72d44318c8f6fb5ea29774000000000000160014139cc0186ad9fefb2ceb1ce77ef6a7eb960ad05d0248304502210090508855aaca0dc1a5387d136ca4373791855792c3467a39991aca10d4814639022057312bedf9d922999ccba8dab4b8f3d5a32548c4c380e9f316a93e347e36e88d0121034c30836747cd7d7a50db1de9ceb1c953a4c308bbf2669c620d62a4370f63b47200000000
```

再度送金

```
$ bcli sendrawtransaction 02000000000101387eee5dd96c6349fb16ceef466c0b9265e029cd71abe47d7ee0a08621424c360000000000ffffffff027011010000000000220020575a24105c2477064a499c43645d344cdcb76cd847dd1c72d44318c8f6fb5ea29774000000000000160014139cc0186ad9fefb2ceb1ce77ef6a7eb960ad05d0248304502210090508855aaca0dc1a5387d136ca4373791855792c3467a39991aca10d4814639022057312bedf9d922999ccba8dab4b8f3d5a32548c4c380e9f316a93e347e36e88d0121034c30836747cd7d7a50db1de9ceb1c953a4c308bbf2669c620d62a4370f63b47200000000
```

```
19fdd3ba91d7b7e2a05a49cdf20998334d5e5a774025e8cb51bfc2c4d06c364b
```

得られたトランザクションをExplorerで確認してみましょう。

https://explorer.bc-2.jp/tx/19fdd3ba91d7b7e2a05a49cdf20998334d5e5a774025e8cb51bfc2c4d06c364b