bitcoinjs-libでP2SH-P2WSHのトランザクションを作る

# はじめに

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

こちらと同じように、P2SH-P2WSHのトランザクションを作成します。

Signetフルノードの動かし方などは上記記事を参考にしてください。

# 準備

## 鍵

鍵はこちらと同じjsonファイルを使います。

https://qiita.com/shu-kob/items/274f499cba27ab682c8c#%E9%8D%B5%E3%81%AE%E6%BA%96%E5%82%99


## BTCをアドレスに入れておく

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

こちらで作成したアドレスにsBTCを送っておきます。

```
P2SH-P2WSH:
2N9mgCypNXq8qkx1SHqBkDW4u92rXKsdJjH
```

https://explorer.bc-2.jp/tx/f77f76cbc68444716894f5884f99e281c943e7f1832886bc8030cda5d05e803d

TXは
f77f76cbc68444716894f5884f99e281c943e7f1832886bc8030cda5d05e803d
Indexは1です。

## RawTxを取得

資金源のTxのRawTxを取得します。

```
bcli getrawtransaction f77f76cbc68444716894f5884f99e281c943e7f1832886bc8030cda5d05e803d
```

```
02000000000101de2a2fc215b03e798e4b25b313c595a207621798991c1bc3526b688b78d852bb0000000000feffffff0285f29000000000001600146f5fcbf72dd6bce849eb78dc5b984f5cdc69ec0ca08601000000000017a914b54460b9b1339d23d349cdd216ab44314cbeca2a870247304402206a7c7ae6b7135b8091467d72869ef6ee01b5d53ebf24c75e79b0d339c672d5af02206ed9c92a56e91c3ac8afc8abae493e84f59b6a76edbe32360091470bb00b39440121034e87fe334e75b2a1ccb6ef9a6b2beac216d7763f321eeb75c1191889ce191a498ece0000
```

# トランザクション作成

previousRawTxに前節で得たRawTxを入れます。

```
const previousRawTx = '02000000000101de2a2fc215b03e798e4b25b313c595a207621798991c1bc3526b688b78d852bb0000000000feffffff0285f29000000000001600146f5fcbf72dd6bce849eb78dc5b984f5cdc69ec0ca08601000000000017a914b54460b9b1339d23d349cdd216ab44314cbeca2a870247304402206a7c7ae6b7135b8091467d72869ef6ee01b5d53ebf24c75e79b0d339c672d5af02206ed9c92a56e91c3ac8afc8abae493e84f59b6a76edbe32360091470bb00b39440121034e87fe334e75b2a1ccb6ef9a6b2beac216d7763f321eeb75c1191889ce191a498ece0000';
```

TX
f77f76cbc68444716894f5884f99e281c943e7f1832886bc8030cda5d05e803d
Index:1をpsbt.addInputに入れます。

```
psbt.addInput({
    hash: 'f77f76cbc68444716894f5884f99e281c943e7f1832886bc8030cda5d05e803d',
    index: 1,
```

宛先、送金額は任意でセットします。ただし、Input - OutputであるFeeが1satoshi/byteでなくてはなりません。

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

const xpriv1 = require('./xpriv1.json').xpriv;
const xpriv2 = require('./xpriv2.json').xpriv;
const xpriv3 = require('./xpriv3.json').xpriv;

function getPrivkeyFromXpriv(xpriv) {
    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);
    const obj = wif.decode(privateKey_wif);
    const privkey = bitcoin.ECPair.fromPrivateKey(obj.privateKey);
    return privkey;
}

const privkey1 = getPrivkeyFromXpriv(xpriv1);
const privkey2 = getPrivkeyFromXpriv(xpriv2);
const privkey3 = getPrivkeyFromXpriv(xpriv3);

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 pubkey1 = getPubkeyFromXpub(xpub1);
const pubkey2 = getPubkeyFromXpub(xpub2);
const pubkey3 = getPubkeyFromXpub(xpub3);

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

const p2wsh =  bitcoin.payments.p2wsh({
        redeem: bitcoin.payments.p2ms({ m: 2, pubkeys, network: bitcoinNetwork, })
})

const p2sh = bitcoin.payments.p2sh({
    redeem: p2wsh
});

console.log('P2SH-P2WSH address')
console.log(p2sh.address) 

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

const previousRawTx = '02000000000101de2a2fc215b03e798e4b25b313c595a207621798991c1bc3526b688b78d852bb0000000000feffffff0285f29000000000001600146f5fcbf72dd6bce849eb78dc5b984f5cdc69ec0ca08601000000000017a914b54460b9b1339d23d349cdd216ab44314cbeca2a870247304402206a7c7ae6b7135b8091467d72869ef6ee01b5d53ebf24c75e79b0d339c672d5af02206ed9c92a56e91c3ac8afc8abae493e84f59b6a76edbe32360091470bb00b39440121034e87fe334e75b2a1ccb6ef9a6b2beac216d7763f321eeb75c1191889ce191a498ece0000';

psbt.addInput({
    hash: 'f77f76cbc68444716894f5884f99e281c943e7f1832886bc8030cda5d05e803d',
    index: 1,
    redeemScript: p2wsh.output,
    witnessScript: p2wsh.redeem.output,
    nonWitnessUtxo: Buffer.from(previousRawTx, 'hex'),
});
psbt.addOutput({
    address: "tb1q2adzgyzuy3msvjjfn3pkghf5fnwtwmxcglw3cuk5gvvv3ahmt63qqqa3n6",
    value: 30000,
});
psbt.addOutput({
    address: "tb1qzwwvqxr2m8l0kt8trnnhaa48awtq45zauflz5y",
    value: 69776,
});

psbt.signInput(0, privkey2)
psbt.signInput(0, privkey3);

psbt.validateSignaturesOfInput(0, Buffer.from(pubkey2, 'hex'))
psbt.validateSignaturesOfInput(0, Buffer.from(pubkey3, 'hex'))

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

console.log(txHex);
```

コードを実行します。

```
$ node p2shP2wshTx.js 
privateKey_wif:
cVqL1ToSfwLw2xt6AB6bXoN51MrpTKj4RawFN682mD3zockVgqXD
privateKey_wif:
cRbPqcaZ9L9hvGkFkjyALCmb5hvwfkW8j4GhVpFqN1vvmNDGJZ61
privateKey_wif:
cStFWwyLBG6SGtNsqZ5MjnaLdYpsyzu5hp5U11WrL3ZBMPAWshdc
P2SH-P2WSH address
2N9mgCypNXq8qkx1SHqBkDW4u92rXKsdJjH
020000000001013d805ed0a5cd3080bc862883f1e743c981e2994f88f59468714484c6cb767ff70100000023220020575a24105c2477064a499c43645d344cdcb76cd847dd1c72d44318c8f6fb5ea2ffffffff023075000000000000220020575a24105c2477064a499c43645d344cdcb76cd847dd1c72d44318c8f6fb5ea29010010000000000160014139cc0186ad9fefb2ceb1ce77ef6a7eb960ad05d040047304402206def24babcd21f73271892e5e20a37cf24f32c7f548405a880d50f2b4ba0490302207d6f935be1dedb3b4dd0b09231126082be75873ef07f861a51f229789d75a05d01473044022026dfcc2f82eda5cfba13745573fe4521bcc3ee50539cc5b8c605ec4749234c0e022014d665a81548dcef10643a3060239cfc4a0dce13ac292d39543db4890f443eaa01695221036791fd096f2a83f355826012c9a29bc07ea4460cdbc424f57d7b181a502121092102f94b3a35c8464d8fe3a121e685b4f56adff15f5976f57f7b3e176b3b4707ba6121039ff1a14f03925083945d1158bcd7167979f8fb73dc8af80818a178aaaf64fa7553ae00000000
```

得られたRawTxをブロードキャストします。

```
$ bcli sendrawtransaction 020000000001013d805ed0a5cd3080bc862883f1e743c981e2994f88f59468714484c6cb767ff70100000023220020575a24105c2477064a499c43645d344cdcb76cd847dd1c72d44318c8f6fb5ea2ffffffff023075000000000000220020575a24105c2477064a499c43645d344cdcb76cd847dd1c72d44318c8f6fb5ea29010010000000000160014139cc0186ad9fefb2ceb1ce77ef6a7eb960ad05d040047304402206def24babcd21f73271892e5e20a37cf24f32c7f548405a880d50f2b4ba0490302207d6f935be1dedb3b4dd0b09231126082be75873ef07f861a51f229789d75a05d01473044022026dfcc2f82eda5cfba13745573fe4521bcc3ee50539cc5b8c605ec4749234c0e022014d665a81548dcef10643a3060239cfc4a0dce13ac292d39543db4890f443eaa01695221036791fd096f2a83f355826012c9a29bc07ea4460cdbc424f57d7b181a502121092102f94b3a35c8464d8fe3a121e685b4f56adff15f5976f57f7b3e176b3b4707ba6121039ff1a14f03925083945d1158bcd7167979f8fb73dc8af80818a178aaaf64fa7553ae00000000
```

```
475c9caf689b136a3d9ad03a90d66959257812cc9df7f51865bc1df636ad8901
```

送金できました。

https://explorer.bc-2.jp/tx/475c9caf689b136a3d9ad03a90d66959257812cc9df7f51865bc1df636ad8901