Typical Transaction (1 input, 2 outputs) - Native Segwit P2WPKH

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 a typical P2WPKH transaction, spending 1 P2WPKH UTXO and creating 2 new P2WPKH UTXOs, one for the actual payment and one for the change.

Create a UTXO to spend from

Import libraries, test wallets and set the network.
const bitcoin = require('bitcoinjs-lib')
const { alice, bob } = require('./wallets.json')
const network = bitcoin.networks.regtest
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.

Create a bitcoinJS key pair object for alice_1, the only possible spender of our new UTXO.
const keyPairAlice1 = bitcoin.ECPair.fromWIF(alice[1].wif, network)
const p2wpkhAlice1 = bitcoin.payments.p2wpkh({pubkey: keyPairAlice1.publicKey, network})
Create a P2PKH address for the recipient bob_1.
const keyPairBob1 = bitcoin.ECPair.fromWIF(bob[1].wif, network)
const p2wpkhBob1 = bitcoin.payments.p2wpkh({pubkey: keyPairBob1.publicKey, network})
console.log('P2PKH address:')
console.log(p2pkhBob1.address)
Create a BitcoinJS transaction builder object.
const txb = new bitcoin.TransactionBuilder(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 the input.
// txb.addInput(prevTxId, prevOutIndex, sequence, prevOutScript)
txb.addInput('TX_ID', TX_VOUT, null, p2wpkhAlice1.output)

Add a first output locking 0.5 btc to bob_1 P2WPKH recipient address.
Add a second output locking 0.499 btc to alice’s change address.

txb.addOutput(p2wpkhBob1.address, 5e7)
txb.addOutput(p2wpkhAlice1.address, 499e5) (1)
1 Here we use the same address for simplicity but an other one would offer better privacy

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

Alice_1 signs.
// txb.sign(index, keyPair, redeemScript, sign.hashType, value, witnessScript)
txb.sign(0, keyPairAlice1, null, null, 1e8, null)
We don’t have to specify any redeem or witness scripts here, since we are spending a native segwit UTXO. However we still need to sign the input value.
Finally we can build the transaction and get the raw hex serialization.
const tx = txb.build()
console.log('Transaction hexadecimal:')
console.log(tx.toHex())
Inspect the raw transaction, 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
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.

In the vout section we have two witness_v0_keyhash outputs, which is the code name for native Segwit.