Bitcoin Transactions: A Guide for Execution Using bitcoinjs-lib

1. Introduction to Bitcoin Transactions

Bitcoin transactions lie at the heart of the decentralized cryptocurrency ecosystem. They enable the transfer of value from one party to another, ensuring the integrity and security of the network. In this article, we embark on a journey to understand Bitcoin transactions by constructing a transaction from a string preimage. A preimage refers to the data that is input into a hash function to calculate a hash.

2. Prerequisites and Tools

Before we dive into the technical details, let’s ensure you have the necessary tools set up:

Installing bitcoinjs-lib

The bitcoinjs-lib library simplifies Bitcoin transaction handling in JavaScript. In this article, we assume that you have Node.js installed on your stem. To install the bitcoinjs-lib library, follow these steps:

  1. Open your terminal or command prompt.

  2. Run the following command:

     npm install bitcoinjs-lib
    
  3. With bitcoinjs-lib, you will be equipped to build, sign, and broadcast Bitcoin transactions.

3. Understanding Unspent Transaction Outputs (UTXOs)

UTXOs play a fundamental role when it comes to Bitcoin transactions. But what are UTXOs, you may wonder? Think of UTXOs as the leftover change you receive after making a purchase. Imagine that you had 10 coins in your wallet, and you spent 4 to buy a cup of coffee. Then the remaining 6 coins become UTXOs.

These UTXOs are like unspent coins waiting to be used in subsequent transactions.

So, why are UTXOs important in Bitcoin transactions? Once a UTXO is spent, it cannot be used again. This property enhances security because it prevents double-spending. Double spending occurs when someone tries to spend the same coins twice by creating multiple transactions. UTXOs prevent this by ensuring that each coin can only be used once.

While UTXOs prevent direct reuse of spent coins within a single transaction, it is crucial to note that their value can be combined with other UTXOs to form new transactions, ensuring the integrity of the Bitcoin network by mitigating the risk of double-spending

UTXOs are also important for tracking the ownership of coins since they represent ownership of specific amounts of Bitcoin. When you receive coins, such as when someone sends Bitcoin to your address, new UTXOs are created in your name. The blockchain keeps track of all UTXOs, maintaining a transparent record of ownership changes.

Now that we have grasped the significance of UTXOs and their role in Bitcoin transactions, let’s dive into the technical nitty-gritty.

4. Creating a Redeem Script from Preimage

In this section, we’ll walk through the process of encoding the preimage (“Learn Bitcoinjs”) into bytes and constructing the redeem script using the OP_SHA256 operation.

1. Setting Up Your Project

  1. Create a Project Directory:

    Open your terminal or command prompt. Navigate to the directory where you want to create your project. Run the following commands and name your project as you like:

    •                     mkdir bitcoin-transaction
                          cd bitcoin-transaction
      
  2. Initialize a Node.js Project:

    Run the following command to create a package.json file:

    •                     npm init -y
      
  3. Install Dependencies:

    Install the bitcoinjs-lib library, which we will use for Bitcoin-related operations:

    •                     npm install bitcoinjs-lib
      
  4. Open the Project in Visual Studio Code (VS Code):

    Since we will be using Visual Studio code as our code editor. Now run:

    •                   code .
      

This opens VS Code with your project folder.

2. Encoding the Preimage into Bytes

We will now create a JavaScript function to encode our preimage into bytes. We will assume that the preimage is a string ('Learn Bitcoin').

On your project directory, create a file transactionBuilder.js.

//Import all the requisite libraries/modules
const bitcoin = require('bitcoinjs-lib');
const { testnet } = require('bitcoinjs-lib/src/networks');
const buff = require('buffer').Buffer; 
const ECPairFactory = require('ecpair');
const tinysecp = require('tiny-secp256k1');
const ECPair = ECPairFactory.ECPairFactory(tinysecp);

// Using the preimage 'Learn Bitcoin', encode it in bytes
const preImageString = 'Learn Bitcoin';
const preimageBytes = buff.from(preImageString, 'utf8');
// Hash the preimage using SHA256
const hashedPreimage = bitcoin.crypto.sha256(preimageBytes);
// Ensure the hashed preimage is 20 bytes (160 bits)
if (hashedPreimage.length > 20) {
  // Truncate the hash to 20 bytes
  const truncatedHashedPreimage = hashedPreimage.slice(0, 20);
  // Reverse the byte order (little-endian)
  const reversedHashedPreimage = buff.from(truncatedHashedPreimage.reverse());

3. Constructing the Redeem Script

Now let’s construct the redeem script using the OP_SHA256 operation. We will use the <reversedHashedPreimage> obtained earlier.

// Create the redeem script: OP_PUSHDATA <reversedHashedPreimage>
  const redeemScript = buff.concat([
    buff.from('a8', 'hex'), // OP_HASH160
    buff.from('14', 'hex'), // Push 20 bytes
    reversedHashedPreimage,
    buff.from('87', 'hex') // OP_EQUAL
  ]);

In the next step, we will explore how to derive a Bitcoin address from the redeem script generated in the previous step using the P2SH (Pay-to-Script-Hash) method.

Step 5: Deriving a Bitcoin Address from the Redeem Script

Before we derive an address, let us have a quick introduction to bitcoin addresses.

What Is a Bitcoin Address?

Think of a Bitcoin address as your digital mailbox. It’s where you receive and send Bitcoins. When someone wants to send you Bitcoins, they use your address as the destination. There are various types of addresses such as Pay-to-Public Key Hash (P2PKH), Pay-to-Script-Hash(P2SH), and Pay-to-Witness-Key-Hash (P2WKH), among others.

A P2SH address is created by hashing a redeem script. Think of a redeem script as a secret recipe for spending Bitcoins. It specifies conditions for spending Bitcoins sent to the P2SH address. Imagine it as coded instructions: “To unlock these Bitcoins, do X, Y, and Z.”

We need to hash the SHA256 hash of the redeem script using RIPEMD160 and the resulting hash is encoded in P2SH format. The resulting hash becomes your P2SH address. The generated P2SH address should start with the number ‘3’ as shown in the following example: 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy

// Derive a P2SH address from the redeem script
  const p2shAddress = bitcoin.payments.p2sh({ redeem: { output: redeemScript } }).address;
  console.log(`P2SH Address: ${p2shAddress}`);
} else {
  // If already 20 bytes, proceed without truncation
  const reversedHashedPreimage = buff.from(hashedPreimage.reverse());
  const encodedPreimage = bitcoin.address.toBase58Check(reversedHashedPreimage, 0x6F);

  // Generate a redeem script from the encoded preimage
  const redeemScript = buff.concat([
    buff.from('a8', 'hex'),
    reversedHashedPreimage, // Use the reversed hash directly
    buff.from('87', 'hex')
  ]);

  console.log(`P2SH Address: ${p2shAddress}`);
}

Step 6: Constructing a Testnet Transaction to Send Bitcoins

Now let's create a Bitcoin transaction that sends testnet Bitcoins to the address we derived in Step 5. We’ll use the P2SH (Pay-to-Script-Hash) address we generated.

First, we will generate a random Private key using the bitcoinjs-lib library. Use the private key to derive the Public Key, which will be used to verify the transaction as shown below:

// Generate a random private key
const keyPair = ECPair.makeRandom();

// Get the private key in Wallet Import Format (WIF)
const privateKeyWIF = keyPair.toWIF();
// Derive the public key from the private key
const publicKey = keyPair.publicKey;

console.log('Private Key (WIF):', privateKeyWIF);
console.log('Public Key (hex):', publicKey.toString('hex'));

The next step is to create a transaction that sends testnet bitcoins to the P2SH address we derived earlier. For this article, testnet bitcoins were received from Coinfaucet to a testnet wallet address.

// UTXO details
const prevHash = '06c53950b25f89adb9b7395c17cbcdac13aebeff98d7dff431e26bb905357226';
const outputIndex = 0;
const outputValue = 3974145949; // Satoshis (e.g., 0.03974146 BTC)

// Recipient addresses
const recipientAddress1 = '35cB7UGskXDnMpCdAp7wofUkDSLAUMtKHD'; // P2SH address
const recipientAddress2 = '2NAdnTLBuizxj9EGuLVPeTLuMS5ukWpTyz8';

// Create an ECPair from the private key
const keyPair = bitcoin.ECPair.fromPrivateKey(Buffer.from(privateKeyHex, 'hex'), { network: bitcoin.networks.testnet });

// Create the transaction builder
const txb = new bitcoin.TransactionBuilder(bitcoin.networks.testnet);

// Add the input (UTXO) to the transaction
txb.addInput(prevHash, outputIndex);

// Add the outputs (recipient addresses)
txb.addOutput(recipientAddress1, outputValue - 39741459); // Deduct the 1% fee
txb.addOutput(recipientAddress2, 62841);

// Sign the input with the private key
txb.sign(0, keyPair);

// Build the transaction
const tx = txb.build();

// Serialize the transaction
const txHex = tx.toHex();

console.log('Raw Transaction:');
console.log(txHex);

Step 7:Broadcast the Transaction

Upon successfully constructing the transaction, the next crucial step is to broadcast it to the testnet network. Broadcasting ensures that your transaction is included in the blockchain and becomes a permanent record.

To do this, use a testnet faucet (such as testnet-faucet.com/btc-testnet) or any other method that allows you to submit your transaction hex to the testnet network.

Testnet faucets are convenient because they provide an easy way to broadcast transactions without running a full Bitcoin node.

To submit the transaction hex, visit a testnet faucet website and look for an option to submit a transaction or broadcast a transaction. Paste your transaction hex (the output from your code) into the provided field. Follow any additional instructions on the website to complete the process.

Once you’ve successfully broadcasted the transaction, you can verify its status using a block explorer (such as blockstream.info/testnet). Search for your transaction hash (txid) to confirm that it has been included in a block.

Conclusion

By now, you have now mastered the art of creating transactions using BitcoinJS. By following these steps, you have learned how to generate an address, a random private key, construct a transaction, and broadcast it to the testnet network. Keep exploring the fascinating world of Bitcoin development, and consider building real-world applications or contributing to the Bitcoin ecosystem.

If you found this article helpful, please share your feedback and encourage others to dive into Bitcoin development.

0
Subscribe to my newsletter

Read articles from Gilbert Cheruiyot directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gilbert Cheruiyot
Gilbert Cheruiyot