Blockchain 0x500 – Mining and Rewards
Thilan Dissanayaka Web3 Development May 25, 2020

Blockchain 0x500 – Mining and Rewards

We’ve come a long way in this series. We built blocks and chains, added transactions, secured them with digital signatures, and even distributed our blockchain across multiple nodes. But there’s a question we’ve been quietly ignoring this whole time.

Why would anyone mine a block?

Think about it. Mining requires serious computational effort — your CPU is brute-forcing millions of nonces to find a hash that starts with the right number of zeros. Nobody’s going to donate that kind of processing power for free.

In a real blockchain like Bitcoin, miners are incentivized with mining rewards — newly minted coins that are created out of thin air and given to the miner who successfully mines a block. This is how new coins enter circulation, and it’s the economic engine that keeps the whole system running.

In this tutorial, we’re going to implement mining rewards and account balances in our blockchain. Let’s get into it.

How Mining Rewards Work

The concept is beautifully simple:

  1. A miner successfully mines a new block.
  2. The system creates a special transaction — a coinbase transaction — that sends a fixed reward to the miner’s address.
  3. This transaction is included in the next block.
  4. The miner’s balance increases by the reward amount.

In Bitcoin, the mining reward started at 50 BTC per block and halves every 210,000 blocks (roughly every 4 years). This is called the halving. It’s why Bitcoin has a capped supply of 21 million coins — the rewards keep getting smaller until they eventually reach zero.

For our blockchain, we’ll keep things simple — a fixed reward of 100 coins per block.

The Coinbase Transaction

A coinbase transaction is special. Unlike normal transactions, it has no sender — the coins are created from nothing. In our system, we’ll represent this by using null as the sender.

We need to update our Transaction class to handle this:

public boolean isValid() {
    // Coinbase transactions (mining rewards) have no sender
    if (sender == null) {
        return true;
    }

    if (signature == null)
        return false;
    return CryptoUtils.verifySignature(sender, getData(), signature);
}

The only change here is that we skip signature verification for coinbase transactions. Since there’s no sender, there’s no private key to sign with. The system itself creates these transactions — they don’t need cryptographic proof of ownership.

Updating the Chain Class

This is where most of the changes happen. Our Chain class needs to know about mining rewards and the miner’s address.

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

    public Chain() {
        blockchain.add(new Block(0, new ArrayList<>(), difficulty, "0"));
    }

    // ... existing methods stay the same
}

We’ve added a miningReward constant — 100 coins for every block mined. Now let’s update the minePendingTransactions() method to accept a miner’s address and create a reward:

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

    // Create the mining reward transaction for the NEXT block
    Transaction rewardTx = new Transaction(null, minerAddress, miningReward);
    transactionPool.addTransaction(rewardTx);
}

Notice something important — the reward transaction goes into the next block’s transaction pool, not the current one. Why? Because the current block is already mined. The miner gets their reward when the next block is mined. This is how Bitcoin does it too — the coinbase transaction is always at the top of the next block.

Wait, but our Transaction constructor expects a PublicKey for the sender, and we’re passing null. We need to update the Transaction class to accept that:

public Transaction(PublicKey sender, PublicKey recipient, double amount) {
    this.sender = sender;
    this.recipient = recipient;
    this.amount = amount;
}

Yes — we’ve also changed the recipient from a String to a PublicKey. In a real blockchain, both sender and receiver are identified by their public keys (addresses). This makes the system consistent and sets us up for proper balance calculation.

Calculating Balances

Right now, our blockchain has no concept of “how much money does Alice have?” Transactions go in, but nobody’s keeping score. Let’s fix that.

The approach is simple — to find someone’s balance, we scan the entire blockchain and look at every transaction:

  • If they’re the recipient, add the amount.
  • If they’re the sender, subtract the amount.
public double getBalance(PublicKey address) {
    double balance = 0;

    for (Block block : blockchain) {
        for (Transaction tx : block.getTransactions()) {
            if (tx.getSender() != null && tx.getSender().equals(address)) {
                balance -= tx.getAmount();
            }
            if (tx.getRecipient().equals(address)) {
                balance += tx.getAmount();
            }
        }
    }

    return balance;
}

This is exactly how Bitcoin works under the hood. There are no “accounts” with stored balances — your balance is calculated by scanning the entire chain and tallying up all your incoming and outgoing transactions. This model is called UTXO (Unspent Transaction Output), and while our implementation is a simplified version of it, the principle is the same.

Preventing Overdrafts

Now that we can calculate balances, we should prevent people from spending more than they have. Let’s update addTransaction():

public void addTransaction(Transaction tx) {
    if (!tx.isValid()) {
        System.out.println("Invalid transaction! Rejected.");
        return;
    }

    // Coinbase transactions don't need balance checks
    if (tx.getSender() != null) {
        double senderBalance = getBalance(tx.getSender());
        if (senderBalance < tx.getAmount()) {
            System.out.println("Insufficient funds! Rejected.");
            return;
        }
    }

    transactionPool.addTransaction(tx);
}

Two validation steps now:

  1. Signature check — is the transaction properly signed? (handled by isValid())
  2. Balance check — does the sender have enough funds? We skip this for coinbase transactions since they create money from nothing.

If Alice has 100 coins and tries to send 150, the transaction gets rejected before it even enters the pool. No more spending money you don’t have.

Putting It All Together

Let’s see the complete flow in action:

public class Main {
    public static void main(String[] args) {
        Wallet alice = new Wallet();
        Wallet bob = new Wallet();
        Wallet miner = new Wallet();

        Chain blockchain = new Chain();

        // Mine the first block — miner gets 100 coins as reward
        blockchain.minePendingTransactions(miner.getPublicKey());

        System.out.println("Miner balance: " + blockchain.getBalance(miner.getPublicKey()));
        // Output: Miner balance: 0.0
        // (reward is in the pool, not yet mined)

        // Mine again to confirm the reward
        blockchain.minePendingTransactions(miner.getPublicKey());

        System.out.println("Miner balance: " + blockchain.getBalance(miner.getPublicKey()));
        // Output: Miner balance: 100.0

        // Miner sends 50 coins to Alice
        Transaction tx1 = new Transaction(miner.getPublicKey(), alice.getPublicKey(), 50);
        tx1.signTransaction(miner.getPrivateKey());
        blockchain.addTransaction(tx1);

        // Alice sends 20 coins to Bob
        Transaction tx2 = new Transaction(alice.getPublicKey(), bob.getPublicKey(), 20);
        tx2.signTransaction(alice.getPrivateKey());
        blockchain.addTransaction(tx2);

        // Mine to confirm these transactions
        blockchain.minePendingTransactions(miner.getPublicKey());

        System.out.println("Miner balance: " + blockchain.getBalance(miner.getPublicKey()));
        System.out.println("Alice balance: " + blockchain.getBalance(alice.getPublicKey()));
        System.out.println("Bob balance: " + blockchain.getBalance(bob.getPublicKey()));
        // Miner: 150 (100 from first reward + 100 from second - 50 sent to Alice)
        // Alice: 30 (50 received from miner - 20 sent to Bob)
        // Bob: 20 (received from Alice)

        // Try to overspend — Bob tries to send 100 but only has 20
        Transaction badTx = new Transaction(bob.getPublicKey(), alice.getPublicKey(), 100);
        badTx.signTransaction(bob.getPrivateKey());
        blockchain.addTransaction(badTx);
        // Output: Insufficient funds! Rejected.
    }
}

Let’s trace through what happens:

  1. First mine — miner mines a block. The reward (100 coins) goes into the transaction pool for the next block. Miner’s balance is still 0.
  2. Second mine — the reward transaction from the first mine is now included in this block. Miner’s balance is 100.
  3. Miner sends 50 to Alice, Alice sends 20 to Bob — both transactions enter the pool.
  4. Third mine — both transactions are confirmed. Miner gets another 100 reward (pending). Balances update: Miner has 150, Alice has 30, Bob has 20.
  5. Bob tries to overspend — he has 20 coins but tries to send 100. The balance check catches it and rejects the transaction.

The system works. Money is created through mining, transferred through signed transactions, and nobody can spend more than they have.

The Economics of Mining

Let’s take a step back and appreciate what we’ve built from an economic perspective.

In our blockchain, the only way new coins enter the system is through mining rewards. This means:

  • Miners have an incentive to keep the network running — they get paid for their work.
  • The money supply is controlled — coins aren’t created arbitrarily. They’re minted at a predictable rate.
  • Transaction processing has a cost — miners spend computational power, and they’re compensated for it.

In Bitcoin, this system also includes transaction fees — a small fee attached to each transaction that goes to the miner. As the block reward halves over time and eventually reaches zero, transaction fees become the primary incentive for miners. We won’t implement fees in this tutorial, but the concept is straightforward — add a fee field to the Transaction class and include it in the miner’s reward.

Wrapping Up

Here’s what we added in this tutorial:

  • Coinbase transactions — special transactions with no sender that create new coins
  • Mining rewards — miners receive 100 coins for each block they mine
  • Balance calculation — scan the entire chain to compute any address’s balance
  • Overdraft prevention — transactions are rejected if the sender doesn’t have enough funds

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

If you’ve followed this entire series from the fundamentals to here, you now have a solid understanding of how blockchains actually work — not just the theory, but the real implementation. Hashing, mining, transactions, digital signatures, multi-node networking, consensus, rewards, and balances. We built all of it from scratch.

Alice, Bob, Trudy, and Eve finally have their trustless bank. No central authority, no single point of failure, and nobody can cheat the system. Not bad at all.

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.