Blockchain 0x200 – Introducing Transactions to the Core Blockchain
Thilan Dissanayaka Web3 Development May 23, 2020

Blockchain 0x200 – Introducing Transactions to the Core Blockchain

In the last tutorial, we built a functioning blockchain from scratch — blocks, hashing, mining, chain validation, the whole thing. But if you remember, our blocks were storing simple strings as data. That’s fine for a demo, but it’s not how real blockchains work.

Think back to Alice, Bob, Trudy, and Eve. Their whole goal was to build a banking system — to send money to each other and keep track of transactions. Right now, our blockchain can’t do that. It just stores arbitrary text.

So in this tutorial, we’re going to fix that. We’ll extend our blockchain to hold actual transactions — who sent what to whom, and how much. Let’s get into it.

What We Need

To make our blockchain transaction-aware, we need a few things:

  • A Transaction class that represents actual transfers between users.
  • A modified Block class that holds multiple transactions instead of a single string.
  • A TransactionPool to temporarily store unconfirmed transactions before they’re mined.
  • An updated Chain class that mines blocks containing transaction data.

wqbf2imlvfmrldxairup.png

Let’s build each one.

The Transaction Class

Let’s start with a very simple transaction model. Each transaction records three things:

  • from — the sender
  • to — the receiver
  • amount — how much is being transferred

In a later part, we’ll introduce proper cryptographic addresses for users. In that case, these sender and receiver fields will be replaced by their specific addresses. For now, let’s keep it simple and use strings to represent their names.

public class Transaction {
    private String from;
    private String to;
    private double amount;

    public Transaction(String from, String to, double amount) {
        this.from = from;
        this.to = to;
        this.amount = amount;
    }

    @Override
    public String toString() {
        return from + " -> " + to + ": " + amount;
    }
}

Pretty self-explanatory, right? No big deal here. We’ve got a constructor that sets the sender, receiver, and amount, plus a toString() method that gives us a readable representation like Alice -> Bob: 10.0.

It’s a minimal structure, but good enough for our purposes. Later, we’ll upgrade this with digital signatures to prove ownership and prevent tampering — but one step at a time.

Modifying the Block Class

In our old blockchain, each block stored a single string like "Alice sends $5 to Bob". Now, we’ll replace that with a list of Transaction objects.

The key change is in the properties. Instead of:

private String data;

We now have:

private ArrayList<Transaction> transactions;

And the constructor changes accordingly — it now accepts a list of transactions instead of a string:

public Block(int index, ArrayList<Transaction> transactions, int difficulty, String prevHash) {
    this.transactions = transactions;
    this.difficulty = difficulty;
    this.prevHash = prevHash;

    this.timestamp = Instant.now().toEpochMilli();

    mineBlock();
}

The other big change is in our calculateHash() method. We need to convert all transactions into a string before hashing:

public String calculateHash() {
    String data = "";
    for (Transaction tx : transactions) {
        data += tx.toString();
    }

    String dataToHash = data + nonce + difficulty + timestamp + prevHash;
    this.hash = CryptoUtils.applySha256(dataToHash);
    return hash;
}

What’s happening? We’re iterating through all the transactions in the block, concatenating their string representations, and then hashing the whole thing along with the nonce, difficulty, timestamp, and previous hash — same as before, just with richer data.

Each block now contains a list of transactions, which are all hashed and secured during mining. If anyone tries to change even one transaction amount or swap a sender’s name, the hash breaks.

Transaction Pool

Now here’s where it gets interesting.

In our blockchain so far, transactions go directly into a block the moment they’re created. But that’s not how real blockchains work. In a real network like Bitcoin or Ethereum, transactions first enter a temporary storage area called the transaction pool (or mempool) before being added to a block.

tthr9bhuoukmrax2lgir.png

This pool stores all unconfirmed transactions — those that have been broadcast to the network but not yet mined into a block. When a miner creates a new block, they pick transactions from this pool, verify them, and include them in the block before mining.

Why Do We Need a Transaction Pool?

Let’s consider the lifecycle of a transaction:

  1. Alice sends 10 coins to Bob.
  2. The transaction is created and broadcast to the network.
  3. All nodes receive the transaction and store it in their transaction pool.
  4. A miner selects transactions from the pool to include in the next block.
  5. Once mined, those transactions are removed from the pool — they’re now confirmed and permanently recorded on the chain.

Without a transaction pool, you’d have to mine a new block for every single transaction. That’s wildly inefficient. The pool lets transactions accumulate, and then a miner batches them together into a single block. This is exactly how Bitcoin works — each block contains hundreds or even thousands of transactions.

This design also allows the blockchain to behave like a decentralized, asynchronous network, where many pending transactions can exist before they’re confirmed.

The TransactionPool Class

Let’s create a new file called TransactionPool.java:

public class TransactionPool {
    private ArrayList<Transaction> pendingTransactions = new ArrayList<>();

    public void addTransaction(Transaction tx) {
        pendingTransactions.add(tx);
    }

    public ArrayList<Transaction> getPendingTransactions() {
        return pendingTransactions;
    }

    public void clear() {
        pendingTransactions.clear();
    }
}

Three simple methods — addTransaction() to queue up a new transaction, getPendingTransactions() to grab the current batch, and clear() to flush the pool after mining. Clean and straightforward.

Updating the Chain Class

Now we need to wire the transaction pool into our Chain class. The chain needs to know about the pool, and it needs a new method to mine all pending transactions into a block.

Here’s the updated Chain.java:

public class Chain {
    private final int difficulty = 4;
    private final ArrayList<Block> blockchain = new ArrayList<>();
    private TransactionPool pool = new TransactionPool();

    public Chain() {
        ArrayList<Transaction> genesisTransactions = new ArrayList<>();
        genesisTransactions.add(new Transaction("system", "genesis", 0));
        blockchain.add(new Block(0, genesisTransactions, difficulty, "0"));
    }

    public void addTransaction(String from, String to, double amount) {
        pool.addTransaction(new Transaction(from, to, amount));
    }

    public void minePendingTransactions() {
        Block block = new Block(
            blockchain.size(),
            pool.getPendingTransactions(),
            difficulty,
            getLatestBlock().getHash()
        );
        blockchain.add(block);
        pool.clear();
    }

    public Block getLatestBlock() {
        return blockchain.get(blockchain.size() - 1);
    }

    // ... isChainValid() and printChain() remain the same
}

What changed? A few things:

  • The chain now holds a TransactionPool instance.
  • addTransaction() is a convenience method — instead of creating Transaction objects externally, we just pass the sender, receiver, and amount.
  • minePendingTransactions() grabs everything from the pool, packs it into a new block, mines it, adds it to the chain, and then clears the pool.
  • The Genesis Block now contains a dummy transaction instead of a plain string.

The flow is now: add transactions → mine pending → repeat. Just like a real blockchain.

Putting It All Together

Let’s see our transaction-aware blockchain in action:

public class Main {
    public static void main(String[] args) {
        Chain blockchain = new Chain();

        // Add some transactions
        blockchain.addTransaction("Alice", "Bob", 10);
        blockchain.addTransaction("Bob", "Trudy", 5);
        blockchain.addTransaction("Trudy", "Eve", 3);

        // Mine the pending transactions into a block
        blockchain.minePendingTransactions();

        // Add more transactions
        blockchain.addTransaction("Eve", "Alice", 7);
        blockchain.addTransaction("Alice", "Trudy", 2);

        // Mine again
        blockchain.minePendingTransactions();

        blockchain.printChain();
    }
}

When you run this, three blocks get mined — the Genesis Block plus two blocks containing our transactions. Each block batches multiple transactions together, just like Bitcoin does. Alice sends to Bob, Bob sends to Trudy, Trudy sends to Eve — all recorded immutably on the chain.

Wrapping Up

Let’s look at what we’ve accomplished in this tutorial:

  • Transaction class — represents a transfer between two parties
  • Updated Block class — now holds a list of transactions instead of a plain string
  • TransactionPool — temporarily stores unconfirmed transactions before mining
  • Updated Chain class — mines pending transactions into blocks in batches

Our blockchain is starting to look like something real. We can create transactions between users, batch them into blocks, and mine them into the chain. That’s a massive step up from storing arbitrary strings.

The full source code is available on GitHub: distributed-blockchain (branch 0x200)

But here’s the elephant in the room — there’s nothing stopping Trudy from creating a transaction that says "Alice -> Trudy: 1000". Anyone can fake a sender right now. That’s a pretty big security hole. In the next article, we’ll fix this by implementing digital signatures and cryptographic wallets. Each user will have a public-private key pair, and only the owner of a private key can authorize transactions from their account. Things are about to get cryptographic.

ALSO READ
Blockchain 0x000 – Understanding the Fundamentals
May 21, 2020 Web3 Development

Imagine a world where strangers can exchange money, share data, or execute agreements without ever needing to trust a central authority. No banks, no intermediaries, no single point of failure yet...

Identity and Access Management (IAM)
May 11, 2020 Identity & Access Management

Who are you — and what are you allowed to do? That's the fundamental question every secure system must answer. And it's exactly what Identity and Access Management (IAM) is built to solve.

How I built a web based CPU Simulator
May 07, 2020 Pet Projects

As someone passionate about computer engineering, reverse engineering, and system internals, I've always been fascinated by what happens "under the hood" of a computer. This curiosity led me to...

Writing a Shell Code for Linux
Apr 21, 2020 Exploit Development

Shellcode is a small piece of machine code used as the payload in exploit development. In this post, we write Linux shellcode from scratch — starting with a simple exit, building up to spawning a shell, and explaining every decision along the way.

Exploiting a Stack Buffer Overflow on Windows
Apr 12, 2020 Exploit Development

In a previous tutorial we discusses how we can exploit a buffer overflow vulnerability on a Linux machine. I wen through all theories in depth and explained each step. Now today we are going to jump...

Access Control Models
Apr 08, 2020 Identity & Access Management

Access control is one of the most fundamental concepts in security. Every time you set file permissions, assign user roles, or restrict access to a resource, you're implementing some form of access control. But not all access control is created equal...

Exploiting a  Stack Buffer Overflow  on Linux
Apr 01, 2020 Exploit Development

Have you ever wondered how attackers gain control over remote servers? How do they just run some exploit and compromise a computer? If we dive into the actual context, there is no magic happening....

Basic concepts of Cryptography
Mar 01, 2020 Cryptography

Ever notice that little padlock icon in your browser's address bar? That's cryptography working silently in the background, protecting everything you do online. Whether you're sending an email,...

Common Web Application Attacks
Feb 05, 2020 Application Security

Web applications are one of the most targeted surfaces by attackers. This is primarily because they are accessible over the internet, making them exposed and potentially vulnerable. Since these...

Remote Code Execution (RCE)
Jan 02, 2020 Application Security

Remote Code Execution (RCE) is the holy grail of application security vulnerabilities. It allows an attacker to execute arbitrary code on a remote server — and the consequences are as bad as it sounds. In this post, we'll go deep into RCE across multiple languages, including PHP, Java, Python, and Node.js.