If you are in the tech industry, then it is almost certain that you have heard about Blockchain technology. Blockchains are the underlying technology behind most Cryptocurrencies and decentralized applications - so called ÐApps. They are also considered to be one of the most important inventions of this generation and quickly gaining popularity among the masses.
Blockchain is the technology that keeps track of all digital data or assets exchanged in the network and this record is called a Ledger. Each data exchanged is a Transaction and every verified transaction gets added as a Block in the ledger. Think of blocks as a box that contains some data and multiple of these blocks are put together to form a blockchain. In the same analogy, the blockchain can be imagined as a container that holds multiple boxes.
Why Blockchain?
Blockchain started in 2008 as a way to store and secure digital currency. It was a part of a proposal made for Bitcoin by Satoshi Nakamoto. Bitcoin was the first application of the Blockchain network. One of its primary benefits is that the recorded information can not be changed without an agreement between all the involved parties or nodes.
Other benefits include:
- Decentralized: Transactions happen on a network of computers.
- Immutability: If a transaction is created, it can not be modified
- Open: All the transactions are visible to all the nodes.
- Security: Due to the encryption feature, Blockchain is almost always secure
Prerequisites
To follow and understand this you should be familiar with these:
- Working knowledge of classes and other ES6 features in Typescript.
- NodeJS installed on the machine.
Getting Started with the Block
We mentioned blocks earlier as a box containing some useful information. I like to think about a blockchain as a LinkedList and each block in the blockchain as a node in the LinkedList. It can be represented as an object in Typescript which contains the following properties:
- Data to record on the blockchain, e.g., transaction data.
- A block hash - which is the ID of the block generated using cryptographic techniques.
- The previous block’s hash in the chain. It is recorded in every block to link it to the chain and improve its security.
- A timestamp of when the block was created and added to the blockchain.
- Proof of Work (PoW), which is the amount of effort taken to derive the current block’s hash (we will be using this consensus since Proof of Stake (PoS) is beyond this article).
Defining the Block Class having the above properties
import { createHash } from 'crypto';
export type Transaction = {
from: string;
to: string;
amount: number;
comment?: string;
};
export type Blockdata = {
transactions: Transaction[];
};
export class Block {
data: Blockdata;
hash: string;
previousHash: string;
timestamp: Date;
pow: number;
constructor(data: Blockdata, previousHash: string) {
this.data = data;
this.hash = '0';
this.previousHash = previousHash;
this.timestamp = new Date();
this.pow = 0;
}
}
Calculating the hash of the Block
The
hash
of the block
is the Identifier generated using the cryptographic technique. We will get the block hash by hashing the current block data, previous block hash, timestamp and PoW using the SHA256 algorithm. We will use crypto, the built-in library of NodeJS for hashing the data.static calculateHash(block: Block): string {
const blockData = JSON.stringify(block.data);
const hashInput = blockData + block.previousHash + block.timestamp.toISOString() + block.pow.toString();
return createHash('sha256').update(hashInput).digest('hex');
}
In the above code, we did the following:
- Converted the block’s data to JSON format so that we can concat it with other information as a string.
- Concatenated the block’s previous hash, data, timestamp, and proof of work (PoW).
- Generating the hash for the earlier concatenation with the SHA256 algorithm.
- Returned the hashing result in base 16, with lowercase letters for A-F.
Mining new Blocks
Mining new blocks involve generating the block’s hash with a certain number of leading zeros. The number of leading zeros is provided by the difficulty level of the current Blockchain. This means that if the blockchain has a difficulty of 4, we have to generate a
block
that starts with four zeros 0000
e.g. 0000a7e42fb6bb92deb28ef7084e362d4fbd150e242167c104cf3fdb4e4f7e39
.Since we derived the
hash
from the block
’s content we can’t change the content but we can certainly increase the proof of work (PoW) value until we satisfy the mining condition.To implement this, we will create a
mine()
method for theBlock
class that will keep incrementing the PoW value and calculating the block hash
until we get a valid hash
.public mine(difficulty: number): void {
// create a regex with template literal string to dynamically reflect the difficulty
const regex = new RegExp(`^(0){${difficulty}}.*`);
// recalculate the hash as long as the hash not matches the regex
while (!this.hash.match(regex)) {
this.pow++;
this.hash = Block.calculateHash(this);
}
}
Define the Blockchain class
As we mentioned earlier a Blockchain is the collection of several
blocks
, the Blockchain
class will have at least three properties i.e. a genesis block
, an array containing other blocks
, and a number denoting the difficulty
level. The genesis block
is the first block
added to the chain
.import { Block, Blockdata } from './Block';
export class Blockchain {
public genesisBlock: Block;
public chain: Block[];
public difficulty: number;
public blockTime: number;
constructor(genesisBlock: Block, chain: Block[], difficulty: number, blockTime: number) {
this.genesisBlock = genesisBlock;
this.chain = chain;
this.difficulty = difficulty;
this.blockTime = blockTime;
}
static create(difficulty: number, blockTime: number): Blockchain {
const genesisBlockData: Blockdata = {
transactions: [{ from: '', to: '', amount: 0 }]
};
const genesisBlock = new Block(genesisBlockData, '');
return new Blockchain(genesisBlock, [genesisBlock], difficulty, blockTime);
}
}
We have also declared a static method inside the Blockchain so that we can initialize a blockchain directly using a difficulty like
const blockchain = Blockchain.create(4)
which will create a Blockchain
instance with a difficulty of 4 along with a genesis block
.Adding new blocks to the blockchain
We have successfully implemented the functionalities for our
blocks
to calculate their hash
and mine themselves. Now let’s define a method inside the Blockchain
class to add new blocks
to the chain
property.public addBlock(data: Blockdata): void {
const blockData = data;
const lastBlock = this.chain[this.chain.length - 1];
const newBlock = new Block(blockData, lastBlock.hash);
// mine the newly created block and add to chain
newBlock.mine(this.difficulty);
this.chain.push(newBlock);
// adjust difficulty according to mining speed
this.difficulty += Date.now() - newBlock.timestamp.getTime() > this.blockTime ? -1 : 1;
}
Explanation of the
addBlock
method:- Collects the details of a transaction (
sender
,receiver
, and transferamount
) from the parameter.
- Creates a new
block
with the transaction details.
- Mines the new
block
with themine
method of the Block class.
- Push the newly created
block
to thechain
property of theblockchain
.
Validating the Blockchain
Now that we have implemented all the functionality of our Blockchain, we need to check for the authenticity of the
blockchain
so that we can validate the blockchain has not been tampered with. We will add the isValid
method to the Blockchain
class.public isValid(): Boolean {
if (this.chain.length === 1) return true;
for (let index = 1; index < this.chain.length; index++) {
const currentBlock = this.chain[index];
const previousBlock = this.chain[index - 1];
if (
currentBlock.hash !== Block.calculateHash(currentBlock) ||
previousBlock.hash !== currentBlock.previousHash
)
return false;
}
return true;
}
Here we recalculated the hash of each
block
on the chain and compared them with the stored hash id and also compare the previousHash
property of the next block
that should be equal to the current block’s hash ID. Since the hash
is calculated using the content of the block
, a slight change in the content will generate a completely different hash
value.Testing the Blockchain
Since we have a fully functioning Blockchain, let’s test all the features we have implemented so far. Create a new
index.ts
file and import the types
and the Blockchain
class. We instantiate a new blockchain
with a difficulty
of 3 through the static method of the Blockchain
class. After that we create two Blockdata
objects to hold some test transactions. Next we use the addBlock
method to add new blocks
with the create Blockdata
to our blockchain
.import { Blockdata } from './Block';
import { Blockchain } from './Blockchain';
// create a new blockchain
const blockchain = Blockchain.create(3 /* difficulty */, 10000 /* blockTime in ms */);
// add transactions for first block data
const firstBlockdata: Blockdata = {
transactions: [
{ from: 'Alice', to: 'Bob', amount: 10 },
{ from: 'Bob', to: 'Alice', amount: 5 }
]
};
// add transactions for second block data
const secondBlockdata: Blockdata = {
transactions: [{ from: 'John', to: 'Jane', amount: 100 }]
};
// add blocks to the chain
blockchain.addBlock(firstBlockdata);
blockchain.addBlock(secondBlockdata);
// log out current state of the blockchain
console.log('Blockchain state after adding two blocks:');
console.dir(blockchain, { depth: null });
// check if blockchain is valid
console.log(`Blockchain valid: ${blockchain.isValid()}`); // true - since we haven't tampered with it
// tampering with the chain, i.e. the evil hacker would do this to get rich
blockchain.chain[1].data.transactions[0] = {
from: 'Alice',
to: 'Bob',
amount: 100,
comment: 'evil hacker tampered the original tx'
};
blockchain.chain[2].data.transactions.push({
from: 'Alice',
to: 'Hacker',
amount: 100,
comment: 'evil hacker injected this tx'
});
// log out state of the blockchain after tampering with it
console.log('Blockchain state after tampering:');
console.dir(blockchain, { depth: null });
// check if blockchain is still valid after tampering with it
console.log(`Blockchain valid: ${blockchain.isValid()}`); // false - tampered with the blockchain
The result should be similar to the image below but expect the
hash
value to be different since at least the timestamp will differ.Bonus: Block time and difficulty adjustment
Block Time is the estimated time it takes for a new
block
to be added to the chain
after mining. It is a constant value. Block time of some common platforms is 10 minutes for Bitcoin and around 13 seconds for Ethereum.Bitcoin adjusts its block time for every 2016 block mined based on the time it took. At the desired rate of one block every 10 minutes, 2016 blocks would take exactly two weeks to find. If the previous 2016 blocks took more than two weeks to find, the difficulty is reduced else increased. The change in difficulty is in proportion to the amount of time over or under two weeks the previous 2016 blocks took to find. It goes like this:
new difficulty = old difficulty * (2016 blocks * 10 minutes) / mining time of the previous 2016 blocks
For our simple blockchain, we will adjust the
difficulty
if the new block
takes more time than the block time. If it takes more time, we will reduce it by 1
else increase it by 1
.We will declare our block time to 10 seconds or 10000 milliseconds. Add the
blockTime
property on the Blockchain
constructor and give it a value like 10000
. Then edit the addBlock
method to adjust the difficulty
after each transaction.Conclusion
Today we learned how blockchains work under the hood and how to create our own Blockchain from scratch using Typescript. The source code is available as a GitHub repository.