Spend a Native Segwit P2WPKH UTXO

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 spend a native Segregated Witness P2WPKH output and create a new legacy P2PKH output.

Creating a UTXO to spend

Send 1 BTC to alice_1 native Segwit P2WPKH address in order to create a P2WPKH UTXO.
sendtoaddress bcrt1qlwyzpu67l7s9gwv4gzuv4psypkxa4fx4ggs05g 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

Creating the transaction

Now let’s spend the UTXO with BitcoinJS.

Import libraries, test wallets and set the network.
const bitcoin = require('bitcoinjs-lib')
const { alice, bob } = require('./wallets.json')
const network = bitcoin.networks.regtest
Create a bitcoinJS key pair object for alice_1, the owner of our new UTXO, and the only one capable of spending it.
const keyPairAlice1 = bitcoin.ECPair.fromWIF(alice[1].wif, network)

In order to create our input we will have to provide the previous output script. We can get it by inspecting the funding transaction at vout  scriptPubKey  hex.

We can also regenerates the output script, a versioned witness program.
const p2wpkhAlice1 = bitcoin.payments.p2wpkh({pubkey: keyPairAlice1.publicKey, network})
console.log('Previous output script:')
console.log(p2wpkhAlice1.output.toString('hex'))

The script is composed as follow: 00 version + PUSHBYTES_14 + public key hash (witness program)

The HASH160 of the public key should match the 20-bytes witness program in the previous output script (the segwit UTXO we are spending).

$ bx bitcoin160 03745c9aceb84dcdeddf2c3cdc1edb0b0b5af2f9bf85612d73fa6394758eaee35d
fb8820f35effa054399540b8ca86040d8ddaa4d5

or

bitcoin.crypto.hash160(Buffer.from('03745c9aceb84dcdeddf2c3cdc1edb0b0b5af2f9bf85612d73fa6394758eaee35d', 'hex')).toString('hex')
Create a PSBT by filling TX_ID and TX_OUT.
const psbt = new bitcoin.Psbt({network})
  .addInput({
    hash: 'TX_ID',
    index: TX_VOUT,
    witnessUtxo: {
      script: Buffer.from('0014' + alice[1].pubKeyHash, 'hex'),
      value: 1e8, (1)
    },
  })
  .addOutput({
    address: bob[1].p2pkh,
    value: 999e5,
  }) (2)
1 In a Segwit transaction we need to pass the value of the previous transaction which will be part of the message to be signed
2 Lock 0.999 BTC to bob_1 public key hash

Let’s calculate our mining fee, subtracting the outputs from the inputs.
100 000 000 - 99 900 000 = 100 000 100 000 satoshis, which is equal to 0,001 BTC.

Alice_1 signs the input and validates the signature
psbt.signInput(0, keyPairAlice1)
psbt.validateSignaturesOfInput(0)
We don’t have to specify any redeem or witness scripts here, since we are spending a native segwit UTXO.
Finalize the PSBT.
psbt.finalizeAllInputs()
Finally we can 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

sendrawtransaction returns the transaction ID, with which you can inspect your transaction again.

getrawtransaction TX_ID true (1)
1 Don’t forget the second argument. If false, it returns the hex string, otherwise it returns a detailed json object.

Observations

In the vin section we note that scriptSig is empty and that we have an additional txinwitness field which contains Alice signature and public key. The semantics of P2WPKH is the same as the semantics of P2PKH, except that the signature is not placed at the same location as before.