A Simple PoW Blockchain Implementation in Java(Part1)

For my project, I’m seeking for solutions to increase the transaction processing throughput of the distributed ledger technology. In order to observe how different factors would affect the Blockchain performance, I would will simulate a Blockchain system with the Proof-of-Work(POW) consensus protocol in Java and all my exploration will be based on that.

First, I will start with the implementation of the fundamental Block data structure and a local Blockchain

A Block is nothing but just a data structure that each instance of it stores some valuable data and some unique identifying information which protects the data from any malicious modification. In the area of cryptocurrency, Block is used to record transactions. Apart from the transactions data, a Block also stores a timestamp which indicates when this block is generated, a hash value of the previously generated Block, and a hash value of its own.

It is containing the hash value of previous Block that links all the Blocks togetherand eventually make them a robust chain. Since each Block’s hash is related to the one before it, modifying any Block will cause a conflict of its hash value recorded by its own and the Block next to it. A real world Block in a PoW-based Blockchain actually still needs to store at least two additional variables: nounce and difficulty to support the PoW mechanism, but I will not include them here and leave them in Part2.

In Java, we will make the Block an object, so let’s write a Block class first:

import java.util.Date;

public class Block {
    public int index;
    public long timestamp;
    public String data;
    public String prev_hash;
    public String hash;

    public Block(int index, String data, String prev_hash) {
        this.index = index;
        this.timestamp = new Date().getTime();
        this.data = data;
        this.prev_hash = prev_hash;
        this.hash = getHash();
    }
}

Now only the hashing algorithm is missing. For the sake of easy future extension on the hashing algorithm, I will also put the hashing method in a separate class: Encrption. Also, for simplicity, in this project, I will follow Bitcoin‘s approach using sha256 for hashing.

Since the sha256 hashing method from Java’s built-in MessageDigest class will outpout the hash in byte type and my Block object holds hash as String, I will also convert the hash from byte to a String of hexadecimal after hashing.
This part is inspired by this page: https://www.baeldung.com/sha-256-hashing-java

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Encryption {
    public static String sha256(String origin) {
        MessageDigest digest = null;
        try {
            digest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        byte[] byteHash = digest.digest(origin.getBytes(StandardCharsets.UTF_8));

        StringBuffer hexHash = new StringBuffer();
        for (int i = 0; i < byteHash.length; i++) {
            String hexDigit = Integer.toHexString(0xff & byteHash[i]);
            if (hexDigit.length() == 1) {
                hexHash.append('0');
            }
            hexHash.append(hexDigit);
        }
        return hexHash.toString();
    }
}

Now, back to the Block class, I will add an getHash() method which will use the Encryption class to hash the Block information:

private String getHash() {
        return Encryption.sha256(this.index + this.timestamp + this.data + this.prev_hash);
    }

By now, the basic Block data structure has been completed. I will build a Blockchain class to bring the blockchain to life. Here I will implement the Blockchain as an ArrayList of Blocks. Besides the blockchain list, I add another list of String, which is used to collect all transactions that are waiting to be validated. I will leave it empty for now.

import java.util.ArrayList;
public class Blockchain {
    public static ArrayList<Block> blockchain = new ArrayList<>(); // The blockchain is implemented as an arraylist of Blocks
    public static ArrayList<String> transactions = new ArrayList<>();
}

Next, to bring the genesis of the blockchain, let’s have a createGenesisBlock() method that generates the first block in the chain for us:

public static Block createGenesisBlock() {
        return new Block(0, "\"Everything starts from here!\"", "0");
    }

As it doesn’t have any previous block, I simply hardcode the previous hash to zero.
Now I need another method that can be used to repeatedly generate new Block based on the lastest Block, this time we need to record the collected transactions information too:

public static Block createNextBlock(Block prevBlock) {
        return new Block(prevBlock.index + 1, transactions.toString(), prevBlock.hash);
    }

Now, every basic functionality of the Block and Blockchain is implemented. To be able to test these visibly, I override the toString() method of the Block class and add a printChain() method to the Blockchain class.

public class Block{
    ... 
    public String toString() {
        return ("Block #" + this.index + "\n\tmined at: " + this.timestamp + "\n\tData: " + this.data + "\n\tHash: {"
                + this.hash + "}\n");
    }
    ...
}
public class Blockchain{
    ... 
    public static void printChain() {
        for (int i = 0; i < blockchain.size(); i++) {
            System.out.println(blockchain.get(i));
        }
    }
    ...
}

Finally, we can test all the works by now. Let’s call createGenesisBlock() in main() and try to add 20 more Blocks to the chain and print out the chain:

public class Blockchain{
    ...
    public final static int NUM_BLOCKS = 20;

    public static void main(String args[]) {
        blockchain.add(createGenesisBlock()); // create genesis block and add it to the chain
        // add 20 more blocks to the chain
        for (int i = 0; i < NUM_BLOCKS; i++) {
            Block next = createNextBlock(blockchain.get(i));
            blockchain.add(next);
        }
        printChain();
    }
}

The output is below:

Block #0
    mined at: 1541753737049
    Data: "Everything starts from here!"
    Hash: {9ccb44f52cbc9fe74a673146bb751b0e280d2644e7cca40fa6ab2de1ef12491f}

Block #1
    mined at: 1541753737059
    Data: []
    Hash: {ca3b31056c8570d154d4342b30568de4bb08053cd6e2c0d942c48fcb08b8ead0}

Block #2
    mined at: 1541753737059
    Data: []
    Hash: {3b8d0515f0b851def18d77d88f1036cae16a69c81209633eada213b2cf0cb561}

Block #3
    mined at: 1541753737059
    Data: []
    Hash: {440d5309fb4273df5657c1d8739d524730b842021ea4fe20a35a40dd67537939}

Block #4
    mined at: 1541753737059
    Data: []
    Hash: {885c9e72a12933d52e3101ffa5fc20baea9a36fa1704016e7c794a57387f3dc4}

Block #5
    mined at: 1541753737060
    Data: []
    Hash: {cb16b9283b669ec6d48a8a424e621b5ab96304f515bb18ae3b8affb3ad42ad50}

Block #6
    mined at: 1541753737060
    Data: []
    Hash: {428b1fec3da2058875da8230d75eae3afacf3ea7110b1b369410cb58736f9d5a}

Block #7
    mined at: 1541753737060
    Data: []
    Hash: {d327e49f238928e77974a7c471a58477b35306563db36aabef65148ee809f3b5}

Block #8
    mined at: 1541753737060
    Data: []
    Hash: {7e82eb0afb71a38c6196a2fe5415691531715378ab85db3f9a5ffd39082a1ab0}

Block #9
    mined at: 1541753737060
    Data: []
    Hash: {a6308867ca8db751a6435a107897c2f9fc0ade5cbe8bf1e0749dedf2eeb08c98}

Block #10
    mined at: 1541753737060
    Data: []
    Hash: {ff955eec0f5cc9903385a7a3b71757f636da24d4f4c382c380761696507899fa}

Block #11
    mined at: 1541753737060
    Data: []
    Hash: {c7d7cfc7baf7822214927776b453a95487754ad842a8fdcb7d3a9376b421d132}

Block #12
    mined at: 1541753737060
    Data: []
    Hash: {5444098344c1d088ac7e6197a34d8320a3d0669cc6af1a1c912e17ca4811679d}

Block #13
    mined at: 1541753737060
    Data: []
    Hash: {dedaf1fbf35ea73c01105cd46260dfbcc503d4994af22bc0a733cf7faf01ae5f}

Block #14
    mined at: 1541753737060
    Data: []
    Hash: {b7f1b23b0dc2b16ac868703b031e270741b0faa911e610ebbc1a6b2747e59213}

Block #15
    mined at: 1541753737060
    Data: []
    Hash: {5dec33bd153bd87ca9e09770a452ed8413b55c560342ff768a3bd54bc89f7940}

Block #16
    mined at: 1541753737062
    Data: []
    Hash: {4e8c715548ede2c257d315c3ad9a67b92f2071d3fb88fbb25d7182f838b27b4e}

Block #17
    mined at: 1541753737062
    Data: []
    Hash: {c56245af9bd5a79e33378c85a8a57c1a15787e88289e02d65869444df0406c35}

Block #18
    mined at: 1541753737062
    Data: []
    Hash: {775434803233007b550958fe519152647b81ae7edf3a3f9ac7f227891c143610}

Block #19
    mined at: 1541753737062
    Data: []
    Hash: {36eaf36426c9bd6e5122fb0f65324c707ded2c7341ef1bfed4c775c259837f88}

Block #20
    mined at: 1541753737062
    Data: []
    Hash: {6546920a8779d82c918e1528ae9c5ee2ec7ab47fc7aca231f7f9a2b7c77d1544}

This result meets my expectation for the simple basic data structure, but some problems is obvious:
1. All data is blank, this is because we doesn’t have any transactions yet;
2. Blocks are generated too quickly, as we can see the timestamp are almost the same for 20 contineous blocks;
3. Coins are still missing.

In part 2, I will complete the implementation to make sure:
1. Transactions broadcasting will be simulated, so that Blocks can record transactions;
2. A Proof-of-Work algorithm will be implemented, so that we can control the Block mining rate;
3. A reward mechanism will be introduced to the Block mining process, miners will be rewarded by coins.

Leave a Reply