I had the opportunity to work with Blockchain lately (on Ethereum) so I learned quickly how is all working, how can I build my own blockchain. The documentation is a little immature at this time, as expected, it’s new technology. Also issues posted online ware broken because of different versions of the tools I used. Most of them weren’t even version 1 yet. I decided to explain the logic behind blockchains, so I will start by building a raw blockchain without using any specific tools. This will be pure Java.
At the base of any blockchain stays the Block
, an entity object containing the data we want to store in the blockchain and some block metadata. Let’s define a Block
class that will contain all the data we need in each block.
public class Block {
/**
* Defines unique ID of block.
*/
private String id;
/**
* Data content associated of this block. E.g. transaction data.
*/
private String data;
/**
* Creation datetime of block.
*/
private LocalDateTime timestamp;
/**
* Block constructor
* @param data payload
*/
public Block(String data) {
this.data = data;
this.id = UUID.randomUUID().toString();
this.timestamp = LocalDateTime.now();
}
// getters and setters here
}
Next let’s create the Blockchain
class that will contain a list of Block
s
public class Blockchain {
private List<Block> chain;
public Blockchain() {
chain = new ArrayList<>();
}
}
Great, now we have a chain of blocks.
But the main feature of Blockchains is it’s blocks integrity. The blocks in the blockchain can not be modified without invalidating the entire blockchain. At the moment we don’t have any feature to check this blockchain integrity so let’s build that.
To ensure the validity of one Block
we will generate a hash code for it. And to ensure that the correct blocks are in order, for each block we will store the hash code of the previous block.
First let’s add the currentHash
and previousHash
fields to our Block
class.
public class Block {
/**
* Defines unique ID of block.
*/
private String id;
/**
* Data content associated of this block. E.g. transaction data.
*/
private String data;
/**
* Creation datetime of block.
*/
private LocalDateTime timestamp;
/**
* Hash for data of the current block.
*/
private String currentHash;
/**
* Hash for data of the previous block.
*/
private String previousHash;
/**
* Block constructor
* @param data payload
*/
public Block(String data) {
this.data = data;
this.id = UUID.randomUUID().toString();
this.timestamp = LocalDateTime.now();
}
/**
* Generate hash code based on the block fields.
* @return [String] generated hash code
*/
public String generateCurrentHashCode() {
String rawData = new StringBuilder()
.append(id)
.append(data)
.append(timestamp)
.append(previousHash)
.toString();
return DigestUtils.sha256Hex(rawData);
}
// getters and setters here
}
The first block in a blockchain is called Genesis and of course it has an empty previousHash
and irrelevant data
. We need our blockchain to generate it at initialization and add it as first block in out blockchain:
public class Blockchain {
private List<Block> chain;
/**
* Blockchain constructor.
*/
public Blockchain() {
chain = new ArrayList<>();
addGenesisBlock();
}
private void addGenesisBlock() {
if (!chain.isEmpty()) throw new IllegalStateException("Chain not empty. Genesis block only allowed on empty chains.");
Block genesis = new Block("Genesis");
genesis.setCurrentHash(genesis.generateCurrentHashCode());
genesis.generateCurrentHashCode();
chain.add(genesis);
}
// getters and setters here
}
Then let’s add these methods to our Blockchain
class to add and get blocks:
/**
* Add a block on top of the chain.
* @param block the block to add.
*/
public void addBlock(Block block) {
block.setPreviousHash(getLastBlock().getCurrentHash());
block.setCurrentHash(block.generateCurrentHashCode());
chain.add(block);
}
/**
* Get the last block on the chain
* @return [Block] last block
*/
public Block getLastBlock() {
return chain.get(chain.size()-1);
}
Now we can create a blockchain instance and add some blocks to it and print everything to see how it all looks like. Let’s a main class.
public class Main {
public static void main(String[] args) {
// initialize blockchain
Blockchain blockchain = new Blockchain();
// add some blocks
Block block1 = new Block("transaction 1 - private data");
blockchain.addBlock(block1);
Block block2 = new Block("transaction 2 - private data");
blockchain.addBlock(block2);
// print all blocks to stdout
blockchain.getChain().stream().forEach(block -> System.out.println(block.toString()));
}
}
Run it and the result is as follows:
0. Block{
id=28a00205-52e4-4a9b-86c8-206c2dc00bca,
data='Genesis',
timestamp=2018-05-12T19:10:26.019,
currentHash='bb7ec73c2bab11adce993764ae2e329fd6a6e3e287e6dbdccb03416acbde479a',
previousHash='null'
}
1. Block{
id=46a02f48-f9ee-4ced-9d38-08277e4b6c43,
data='transaction 1 - private data',
timestamp=2018-05-12T19:10:26.091,
currentHash='8e97fbc621857010b81db8ace5eb40913082c568a4aa483943da5f06f4c26dea',
previousHash='bb7ec73c2bab11adce993764ae2e329fd6a6e3e287e6dbdccb03416acbde479a'
}
2. Block{
id=377eb2a5-3a25-4b5a-ae5b-3fd1ac2e0794,
data='transaction 2 - private data',
timestamp=2018-05-12T19:10:26.092,
currentHash='a0caa2986cf42c46dec6018dacf86aa6c246f09dde245598178ca2cd03a9b57b',
previousHash='8e97fbc621857010b81db8ace5eb40913082c568a4aa483943da5f06f4c26dea'
}
Notice the blocks are connected trough the hashes. block2.previousHash
is equal with block1.currentHash
and block1.previousHash
is equal with genesis block0.currentHash
. This forms the integrity of the blockchain.
By modifying a field in a block, for example block1
, it will invalidate it’s currentHash
. Regenerating the hash for the modified block will result in a different hash. And if block1
will end up with a different currentHash
then it will not match with the next block’s previousHas
(block2.previousHash
). And updating the previousHash
of block2
with the new block1.currentHash
will invalidate the currentHash
of block2
because the previousHash
value is also used when generating the currenthash
. See how they are all connected? Making change to a block will result in regenerating all the hashes for that block and all the blocks after it.
This means that to have a secure blockchain we only need to know the hash of the last block. here block2.currentHash
. Making any change to any block of the blockchain will result in a different hash of the last block.
Our blockchain is ready but there is one last thing to do to have a proper base for a blockchain. We still need a good validator method that will check the integrity of the blockchain. It needs to validate every block by recalculating it’s hash and compare it to the stored currentHash
. And also has to check that each block stores correctly the has of previous block. So let’s implement that next.
public boolean isValid() {
for (int i = 1; i < chain.size(); i++) {
if (!chain.get(i).generateCurrentHashCode().equals(chain.get(i).getCurrentHash())) return false;
if (!chain.get(i).getPreviousHash().equals(chain.get(i-1).getCurrentHash())) return false;
}
return true;
}
Now let’s create the blockchain again an modify the data and check it’s validity.
public static void main(String[] args) {
// initialize blockchain
Blockchain blockchain = new Blockchain();
// add some blocks
Block block1 = new Block("transaction 1 - private data");
blockchain.addBlock(block1);
Block block2 = new Block("transaction 2 - private data");
blockchain.addBlock(block2);
// print all blocks to stdout
blockchain.getChain().stream().forEach(block -> System.out.println(block.toString()));
// check validity
System.out.println("Is blockchain valid? : " + blockchain.isValid()); // => returns true
// modify blocks in blockchain
blockchain.getChain().get(1).setData("different data");
System.out.println("Is blockchain valid? : " + blockchain.isValid()); // => returns false
}
Blockchain integrity was broken. We need to regenerate all hashes and reconnect all previousHash
-currentHash
for all blocks after block1
to get a valid blockchain again. But since the last hash is different this is considered a different blockchain, or a different branch from the main branch for this blockchain.
// modifies all blocks after block1 to get another valid blockchain (or branch of same blockchain).
blockchain.getChain().get(1).setData("different data");
blockchain.getChain().get(1).setCurrentHash(blockchain.getChain().get(1).generateCurrentHashCode());
blockchain.getChain().get(2).setPreviousHash(blockchain.getChain().get(1).getCurrentHash());
blockchain.getChain().get(2).setCurrentHash(blockchain.getChain().get(2).generateCurrentHashCode());
This is the core logic that blockchains are based upon! In the end it is not that complicated at all.
Tips to evolve from here
A few more words I want to add to explain how blockchains are using this core logic in real case scenarios.
It usually works by distributing these blockchains between different nodes
(nodes are machines that hold clones of same blockchain). This way it is avoiding the single point of attack
issue. Meaning that if one blockchain is tampered with, it becomes invalid because all the other clones are different.
Another feature is when creating a new block, the generated hash must be obtained from a different source. This can be done by mining, also called Proof of Work.
But I will stop here and leave these other details for another blog post.
I hope this was informative. Happy coding!