The blockchain technology, which could be part of a digital vehicle record e.g., was one of the main topics we dealt with at e.GO Mobile.

This time, I would like to show how to build a small blockchain using TypeScript, using a simple example focusing on data structure.

The example code can be found on GitHub.

How does a blockchain work?

A blockchain is essentially a container that holds a set of transaction records, along with a few additional pieces of data that serve to verify and secure each block, which are linked to the previous block, forming a chain of blocks that provides an immutable record of all the transactions that have occurred.

Beside the data itself, a block usually contains metadata such as a timestamp, an unique identifier, and a reference to the previous block in the chain.

Implement a block

A block object should implement an interface with the following information:

/**
 * Describes a block in a blockchain.
 */
export interface IBlockChainBlock {
    /**
     * The data in Base64 format.
     */
    readonly data: string;
    /**
     * The hash of this block in Base64 format.
     */
    readonly hash: string;
    /**
     * The zero based index.
     */
    readonly index: number;
    /**
     * The hash of this block in Base64 format.
     */
    readonly previousHash: string;
    /**
     * The time of creation as UNIX timestamp (UTC).
     */
    readonly timestamp: number;
}

Especially the hash property could later be a computed value in the implementaion, that is created from the data of the other properties:

// ...
import crypto from "node:crypto";

// ...

    get hash(): string {
        const sha256 = crypto.createHash('sha256');

        return sha256.update(`${this.index}\n${this.previousHash}\n${this.timestamp}\n${this.data}`)
            .digest()
            .toString('base64');
    }

// ...

Implement the chain

For the chain itself we do not need too much requirements:

  1. it must be iterable, so we are later able to use it in a for loop which return the chain as stream of blocks
  2. it must be work asynchronious, so we can decide later in the implementation, if we want to use Array as “backend” or a databse connection, like MongoDB

The interface could look like this:

/**
 * An asynchronious iterable blockchain.
 */
export interface IBlockChain extends AsyncIterable<IBlockChainBlock> {
    /**
     * Adds a new block to chain.
     *
     * @param {string} data The data in Base64 format.
     *
     * @returns {Promise<IBlockChainBlock>} The promise with the new block.
     */
    addBlock: (data: string) => Promise<IBlockChainBlock>;
}

Create and use a blockchain

For the case of creating an instance of an IBlockChain object we can implement a factory function:

// ...

class ArrayBlockChain implements IBlockChain {
    // ...
}

// ...

/**
 * Creates a new instance of an `IBlockChain`.
 *
 * @returns {Promise<IBlockChain>} The promise with the new instance of a `IBlockChain`.
 */
export async function createNewBlockChain(): Promise<IBlockChain> {
    return new ArrayBlockChain();
}

With all of this, we can now start using the new features:

// create instance
const myBlockchain = new createNewBlockChain();

// add blocks
await myBlockchain.addBlock(
    Buffer.from('test1').toString('base64')
);
await myBlockchain.addBlock(
    Buffer.from('test2').toString('base64')
);

// iterate from beginning to end over
// all blocks in the chain
for await (const block of myBlockchain) {
    console.log(
        'Block', block.index,
        Buffer.from(block.data, 'base64').toString(),
        block.previousHash, block.hash
    );
}

Conclusion and outlook

This example only wants to give an idea how data in a blockchain depends on each other and how the whole chain could be validated later.

In one of the next posts, I will show how to use a database as storage instead of an array.

Have fun while trying it out! 🎉