This page looks best with JavaScript enabled

Blockchain with Node

 ·   ·  ☕ 4 min read

Create your very own mini-blockchain!

We’ll build a very simple POC blockchain with four distinct building blocks.
Check the repo for reference!

Transaction

The only things really needed for a transaction to take place are:

  • Who sends the money?
  • Who receives the money?
  • How much money?
1
2
3
4
5
6
7
export class Transaction {
    constructor(
        public amount: number, 
        public sender: string, 
        public reciever: string
    ) {}
}

Block

Think of a block as a group of transactions with a time stamp and a reference to the previous block:

1
2
3
4
5
6
7
8
9
import {Transaction} from "./Transaction";

export class Block {
    constructor(
        public previousHash: string,
        public transaction: Transaction,
        public timeStamp = Date.now()
    ) {}
}

As discussed before, we need to hash our blocks.
To do this, we’ll use the crypto library:

1
2
3
4
5
6
get hash() {
    const hash = crypto.createHash("SHA256");
    const block= JSON.stringify(this);
    hash.update(block).end();
    return hash.digest("hex");
}

We create a SHA256 hash, add our stringified block to it, and output a hexadecimal representation of it.
This is what will link multiple blocks together.

Chain

Since there should only ever be one chain, we can represent it as a Singleton.
We also know that it will host our blocks and that these need to know about their predecessor, so we can start by:

1
2
3
4
5
6
7
8
9
import {Block} from "./Block";

export class Chain {
    public static instance = new Chain();
    chain: Block[];

    get lastBlock() {
        return this.chain[this.chain.length - 1];
    }

Let us also initiate the chain with a first block:

1
2
3
constructor() {
    this.chain = [new Block("", new Transaction(10, "God", "Satoshi"))];
}

We’ll also need a way to add blocks to the chain. Something like:

1
2
3
4
addBlock(transaction: Transaction) {
    const newBlock = new Block(this.lastBlock.hash, transaction);
    this.chain.push(newBlock);
}

This will work, but it allows anybody to send anything anywhere.
We need verification, signatures and keys.
We need a wallet.

Wallet

At a basic level a wallet is just a wrapper for a key pair, not unlike what you use to secure an SSH connection.

1
2
3
4
5
6
7
8
export class Wallet {
    public pubKey: string;
    public privKey: string;

    get address(){
        return this.pubKey;
    }
}

Again, we’ll use the crypto library to generate the key-pair.
Since we want two ways encryption, we’ll use RSA.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import crypto from "crypto";

constructor() {
    const keyPair = crypto.generateKeyPairSync("rsa", {
        // standard rsa settings
        modulusLength: 4096,
        publicKeyEncoding: {type: 'spki', format: 'pem'},
        privateKeyEncoding: {type: 'pkcs8', format: 'pem'},
    })

    this.pubKey = keyPair.publicKey;
    this.privKey = keyPair.privateKey;
}

Payment

As explained before, we don’t want to expose the private key nor the encrypted data.
Rather, we use the private key to sign the data.

This way, the public key can be used to verify the data’s integrity without exposing the private key.

1
2
3
4
5
6
7
8
9
pay(amount: number,senderPubKey: string) {
    const transaction = new Transaction(amount, this.pubKey, senderPubKey);

    const sign = crypto.createSign("SHA256");
    sign.update(transaction.toString()).end();
    const signature = sign.sign(this.privKey);

    Chain.instance.addBlock(transaction, this.pubKey, signature);
}

Here we create a transaction and use crypto to sign it with the payer’s private key.
In a real world scenario we would verify the signature sending the block to various miners (nodes) over the web, but it’s good enough for our purpose.

We’ll have to update our addBlock function to ensure the block is verified before being added to the chain:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
addBlock(transaction: Transaction, senderPubKey: string, signature: Buffer) {
    const verifier = crypto.createVerify("SHA256");
    verifier.update(transaction.toString());
    const transactionIsValid = verifier.verify(senderPubKey, signature)

    if (transactionIsValid) {
        const newBlock = new Block(this.lastBlock.hash, transaction);
        // Proof of work!
        this.mine(newBlock.proofOfWorkSeed);
        this.chain.push(newBlock);
    }
}

Mining

As mentioned before, we’ll use of the concept of “Proof of Work” to our advantage.

Let’s add a POW seed to our Block class:

1
proofOfWorkSeed = Math.round(Math.random() * 42069666);

Now we can add a mine function to our Chain such as:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
mine(proofOfWorkSeed: number) {
    let solution = 1;
    console.log("⛏️ ... ⛏️ ... ⛏️");

    while (true) {

        const hash = crypto.createHash("MD5");
        hash.update((proofOfWorkSeed + solution).toString()).end();

        const attempt = hash.digest("hex");

        if (attempt.substr(0, 4) === "9999") {
            console.log(`Done! Solution: ${solution}`)
            return solution;
        }

        solution += 1;
    }
}

Here we look for a number that added to the seed will produce a hash starting with four consecutive nines.
The specific implementation here doesn’t really matter, as long as it is somewhat costly to compute.

After finding the correct answer (which will be different for each block) it will return it for the other nodes to verify (which is much easier than to solve), or at least that is what would happen in a real world application.

Run it

Hopefully you’ve ended up with something like this.

You can add your own finishing touches and run npm run start to see it in practice.

There you go, your very own blockchain!

Support the author with