Multi-signature Nested Segwit 2 of 4

To follow along this tutorial

  • Clone the Github repository

  • cd code

  • npm install or yarn install

  • Execute the transaction code by typing node tx_filename.js

  • Alternatively you can enter the commands step-by-step by cd into ./code then type node in a terminal to open the Node.js REPL

  • Open the Bitcoin Core GUI console or use bitcoin-cli for the Bitcoin Core commands

  • Use bx aka Libbitcoin-explorer as a handy complement

Let’s create, fund and spend a 2 of 4 multi-signature with an embedded Segwit P2SH-P2WSH transaction.

Creating and Funding the P2SH

Import libraries, test wallets and set the network.
const bitcoin = require('bitcoinjs-lib')
const {alice, bob, carol, dave} = require('./wallets.json')
const network = bitcoin.networks.regtest
Create the witness script with the p2ms payment method.
const p2ms = bitcoin.payments.p2ms({
  m: 2, pubkeys: [
    Buffer.from(alice[1].pubKey, 'hex'),
    Buffer.from(bob[1].pubKey, 'hex'),
    Buffer.from(carol[1].pubKey, 'hex'),
    Buffer.from(dave[1].pubKey, 'hex'),
  ], network})

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

console.log('HASH160(SHA256(witness script)):')
const hash160sha256 = bitcoin.crypto.hash160(
  Buffer.from('0020' + bitcoin.crypto.sha256(p2ms.output).toString('hex'),
    'hex'))
console.log(hash160sha256.toString('hex')) (1)
console.log()
1 This double hash is contained in the funding tx locking script
Check the witness script.
decodescript SCRIPT

The asm field should contain:

02 03745c9aceb84dcdeddf2c3cdc1edb0b0b5af2f9bf85612d73fa6394758eaee35d 027efbabf425077cdbceb73f6681c7ebe2ade74a65ea57ebcf0c42364d3822c590
023a11cfcedb993ff2e7523f92e359c4454072a66d42e8b74b4b27a8a1258abddd 02e9d617f38f8c3ab9a6bde36ce991bafb295d7adba457699f8620c8160ec9e87a 04
OP_CHECKMULTISIG
Create the P2SH address.
const p2wsh = bitcoin.payments.p2wsh({redeem: p2ms, network})
const p2sh = bitcoin.payments.p2sh({redeem: p2wsh, network})
console.log('P2SH address:')
console.log(p2sh.address)
console.log()
Send 1 BTC to this P2SH address.
sendtoaddress 2N4LnN5rp8JAmqE3LBVQhYEQg83piAF15sX 1
Get the output index so that we have the outpoint (txid / vout).
gettransaction TX_ID
Find the output index (or vout) under details  vout.

Preparing the spending transaction

Let’s now prepare the spending transaction by setting input and output and having two people (private keys) to sign the transaction. In this tutorial, alice_1 and bob_1 will redeem the P2SH-P2WSH multi-signature and send the funds to alice_2 P2WPKH address.

Create the PSBT by filling TX_ID and TX_OUT.
const psbt = new bitcoin.Psbt({network})
  .addInput({
    hash: 'TX_ID',
    index: TX_VOUT,
    redeemScript: Buffer.from('0020' + bitcoin.crypto.sha256(p2ms.output).toString('hex'), 'hex'), (1)
    witnessScript: p2wsh.redeem.output,
    witnessUtxo: {
      script: Buffer.from('a914' + hash160sha256.toString('hex') + '87', 'hex'), (2)
      value: 1e8,
    }
  })
  .addOutput({
    address: alice[2].p2wpkh,
    value: 999e5,
  }) (3)
1 Locking script of the UTXO we are spending, which is a P2WSH witness program, <00> + PushByte 20 + SHA256(witness script)
2 Hash160-SHA256 of the witness script, contained in a P2SH template
3 Output locking the funds to alice_2 P2WPKH address, leaving 100 000 satoshis as mining fees
Prepare signers Alice_1 and Bob_1 keypairs.
const keyPairAlice1 = bitcoin.ECPair.fromWIF(alice[1].wif, network)
const keyPairBob1 = bitcoin.ECPair.fromWIF(bob[1].wif, network)
Alice_1 and bob_1 now sign the same input.
psbt
  .signInput(0, keyPairAlice1)
  .signInput(0, keyPairBob1)
Finalize the PSBT.
psbt.validateSignaturesOfInput(0, Buffer.from(alice[1].pubKey, 'hex'))
psbt.validateSignaturesOfInput(0, Buffer.from(bob[1].pubKey, 'hex'))

psbt.finalizeAllInputs()
Extract the transaction and get the raw hex serialization.
console.log('Transaction hexadecimal:')
console.log(psbt.extractTransaction().toHex())
Inspect the raw transaction with Bitcoin Core CLI, check that everything is correct.
decoderawtransaction TX_HEX

Broadcasting the transaction

It’s time to broadcast the transaction via Bitcoin Core CLI.
sendrawtransaction TX_HEX
Inspect the transaction.
getrawtransaction TX_ID true

Observations

We can see that the scriptSig contains the version byte 00 followed by a 32-bytes witness program, the sha256 of the witness script. The hash160 of this scriptSig (asm version) should match the HASH160 contained in the P2SH UTXO locking script we are spending.

Verify the unlocking script HASH160.
$ bx bitcoin160 '00205b07dcc35fc2b29db80be059e495c88f5b7609c1e3d888c14240678f00217b3d'
79b67d4c7bff512939e90e170ee9b969eb1203a8

or

bitcoin.crypto.hash160(Buffer.from('00205b07dcc35fc2b29db80be059e495c88f5b7609c1e3d888c14240678f00217b3d', 'hex')).toString('hex')

After checking hash equality, the script interpreter recognize that it is actually a Segwit transaction thanks to the version byte and triggers execution of the witness data stack. The witness, located in the txinwitness field contains

  • An empty string that will convert to a dummy but mandatory 00 value due to a bug in OP_CHECKMULTISIG

  • Alice_1 and bob_1 signatures

  • The witness script