Understanding Blockchain by building one
⛓️

Understanding Blockchain by building one

Tags
Blockchain
Blog
Typescript
Published
Feb 8, 2022
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.

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;
    }
}
Block.ts

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');
}
Block.ts
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);
    }
}
Block.ts

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);
    }
}
Blockchain.ts
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;
}
Blockchain.ts
Explanation of the addBlock method:
  • Collects the details of a transaction (sender, receiver, and transfer amount) from the parameter.
  • Creates a new block with the transaction details.
  • Mines the new block with the mine method of the Block class.
  • Push the newly created block to the chain property of the blockchain.

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;
}
Blockchain.ts
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
index.ts
The result should be similar to the image below but expect the hash value to be different since at least the timestamp will differ.
Terminal output
Terminal output

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.