Data Anchoring with OP_RETURN

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 store for eternity some data on the Bitcoin blockchain using the special OP_RETURN opcode. It will create a special nulldata output type.

An output using an OP_RETURN is provably unspendable, so we don’t need to lock any BTC in this UTXO. However, we still need to pay the miner fees, so we will spend BTC from a previous P2WPKH UTXO, create one nulldata UTXO and an other one for the change, leaving the difference as mining fees.

For more information about OP_RETURN check out:

Create a UTXO to spend from

Let’s create a P2WPKH UTXO to spend from.

Import libraries, test wallet and set the network.
const bitcoin = require('bitcoinjs-lib')
const { alice } = require('./wallets.json')
const network = bitcoin.networks.regtest
Send 1 BTC to alice_1 P2WPKH address via Bitcoin Core CLI.
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 create our OP_RETURN P2PKH UTXO.

Create a bitcoinJS key pair object for the spender alice_1 and a P2WPKH address that we will use for the change.
const keyPairAlice1 = bitcoin.ECPair.fromWIF(alice[1].wif, network)
Create a PSBT.
const psbt = new bitcoin.Psbt({network})
Create the input by filling TX_ID and TX_VOUT.
psbt
  .addInput({
    hash: 'TX_ID',
    index: TX_VOUT,
    witnessUtxo: {
      script: Buffer.from('0014' + alice[1].pubKeyHash, 'hex'),
      value: 1e8,
    },
  })
Create an OP_RETURN output with BitcoinJS embed payment method.
const data = Buffer.from('Programmable money FTW', 'utf8')
const embed = bitcoin.payments.embed({data: [data]})

psbt
  .addOutput({
    script: embed.output,
    value: 0,
  })
An output using an OP_RETURN is provably unspendable, the script will always evaluate to false. For this reason, and because this is a Bitcoin Core policy rule, the value of an OP_RETURN output is set to 0.
Create a second output to get back the change. 100 000 000 - 100 000(fees) = 99 900 000 sats
psbt
  .addOutput({
    address: alice[1].p2wpkh,
    value: 999e5,
  })

Keep note of the bitcoin value of the UTXO we are spending, necessary because we are spending a P2WPKH.

Alice_1 signs the first input.
psbt.signInput(0, keyPairAlice1)
Finalize the PSBT.
psbt.validateSignaturesOfInput(0)
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.
sendrawtransaction TX_HEX
Inspect the transaction.
getrawtransaction TX_ID true

Observations

We note that the OP_RETURN UTXO is marked with the special type nulldata.

To decode the OP_RETURN data we can use the xxd library in a terminal which make a hexdump or the reverse.

echo 50726f6772616d6d61626c65206d6f6e65792046545721 | xxd -p -r