Blockchain implementation using JavaScript

This post is 4 years old. (Or older!) Code samples may not work, screenshots may be missing and links could be broken. Although some of the content may be relevant please take it with a pinch of salt.

Blockchain seems to be the buzzword of late, and since it is gaining momentum, it is time for us to investigate it as well. In my opinion, the best way to study a particular technology is to get our hands dirty. Therefore in this article, we'll implement blockchain using JavaScript, and hopefully, while discussing the implementation, we'll also learn the basics of the technology.

What is blockchain?

Let's start with answering this fundamental question. The most straightforward explanation is that it is a distributed and decentralised public ledger. But why is it called "blockchain" and how does it work? So the term "blockchain" comes from the fact that a "block" contains a transaction - a new piece of data, and it can be anything, starting from medical records to voting records. Once we have a block, the next transaction creates another block, and they are all added to a chain, hence the name: blockchain, which is a chain of blocks (or a chain of transactions).

Please note that often blockchain and cryptocurrencies (like Bitcoin) seem to be used interchangeably - this is however not true. The blockchain is the technology that enables cryptocurrencies like Bitcoin to exist in the first place.

Let's talk about this "distributed and decentralised public ledger" concept a bit more. The data held by blockchain is shared, meaning that there's no centralised version of the data available anywhere since the data is distributed amongst multiple (hundreds, thousands or even millions) computers - and the information that is stored is open to everyone.

Let's go ahead and implement what we have discussed so far. Remember, a blockchain is just a chain of blocks, and our implementation also has to show this. Therefore in the class that we'll about to create we will have a method to generate a block and even some functionality in place that will hold all the blocks.

Get the code

The codebase for this article is available on GitHub.

Creating a block

For our purposes a block will contain an index (which is going to be the length of all the blocks from the chain, plus one since the string will be an array and those are 0 indexed), it'll contain a timestamp, only set to be the time of the block creation. It will also include a proof (which we'll discuss later on) and a hash which will be based on the hash of the previous block. (Don't forget that this is the hash that makes sure that items ("blocks") in the blockchain haven't been fiddled with).

Optionally, as mentioned earlier, a block can also contain data. Here's a potential implementation of the block:

createBlock(proof, previousHash = undefined) {
const block = {
index: this.chain.length + 1,
timestamp: new Date().getTime(),
proof,
previousHash: previousHash ? previousHash : this.hash(JSON.stringify(this.lastBlock()))
};

this.chain.push(block);
return block;
}

Notice the reference to the proof in the parameter list as well as a reference to another function for the hash (this.hash()).

Let's first take a look at the hash function. To create a hash of the previously used block, we'll use HMAC SHA256 with a secret. And luckily we can use the crypto package to achieve this, so make sure that first this package is imported:

const crypto = require('crypto');
// ...
hash(block) {
return crypto.createHmac('sha256', 'a').update(block).digest('hex');
}

The purpose of the hash is to make sure that the block and the chain of blocks haven't been fiddled with - meaning that if someone modifies an entry in an earlier hash, it will break the entire chain because the hash is now going to be incorrect.

Please note that the secret a is used here only for demonstration purposes - you should never have such a simple secret in place, and you should never store the secret value in your application code but instead use environment variables.

Mining

To create a new block, we need to mine it. Mining is usually linked to solving a problem which has two properties: it's somewhat difficult to solve, but easy to verify. These problems become more and more complicated after each iteration. In our case, we'll implement a simple mining problem, where we want to find values that, when hashed, start with abcd. The most popular algorithm used by some implementations of Blockchain is called Proof of Work (PoW). Simply put, with a PoW we can create, and solve mathematical puzzles (or problems).

To make the mining easier or more difficult the implementation can be changed - looking for hashes that start with abc only will make the work faster, while increasing it to be abcde will slow it down. Please note that these changes come with significant performance increments/decrements.

Let's take a look at our implementation of this:

proofOfWork(lastProof) {
let proof = 0;
while (!this.validProof(lastProof, proof)) {
proof += 1;
}
return proof;
}

validProof(lastProof, proof) {
let guess = `${lastProof}${proof}`;
let guessHash = crypto.createHmac('sha256', 'a').update(guess).digest('hex');
return guessHash.slice(0, 4) === 'abcd';
}

The above code is very straight forward. We start the proofs from 0, and if we find a number, that, when hashed starts with abcd we return the number. That number is also going to be added to the newly forged block.

Proof of Work

The PoW always creates a cryptographic value - how they are built are beyond the scope of this article - but suffice to say that the same input yields the same output. In our case above we need to go through some iterations taking the last proof value appended with an increasing number to make sure that when the first four values of the appended string start with abcd. This will cause, at all times, a different proof number.

The PoW implementation could be something different as well - for example, take a hash of a word. Keep appending numbers after the word and if the hash of the word plus the attached numbers' first four letters equal to 0000 we have a "proof".

There are some other algorithms to solve problems - another one that pops up often is Proof of Stake (PoS).

Implementing the blockchain

Once the basics of the blockchain class are out of the way, we can create an application that would be using this class. The easiest way would be to go ahead and develop a set of API endpoints that would call the appropriate actions from the class:

  • /mine: an endpoint to mine a new block
  • /chain: returns all the blocks in the implementation

As you can see from the above list, the /chain endpoint will return us all the blocks. But where does our first block come from when we start the application? It is something that we need to create programmatically - and this is called a genesis block.

Genesis block

A "genesis block" is the first block created in the blockchain - this is the 'root' of the chain. All other blocks are created after this one. In our implementation, we'll create this block with value 1 and with a proof of 1 as well. This is simply achieved by calling the createBlock() class method upon class initialisation:

class Blockchain {
constructor(chain = []) {
this.chain = chain;
this.createBlock(1, 1); // genesis block
}
// ... more code

The endpoints

Let's take a look at the implementation of the mine endpoint. In our application, we are not going to be using any third party dependencies (i.e. we won't be using Express) but instead we'll be relying on built-in HTTP methods:

const http = require('http');
const url = require('url');

const Blockchain = require('./blockchain.class');
const blockchain = new Blockchain();

const port = 3000;

const server = http.createServer((req, res) => {
const urlParts = url.parse(req.url);
switch(urlParts.pathname) {
// ... more code

Once we have this code in place, we can create the endpoints.

/chain

This endpoint is responsible for returning the entire chain.

case '/chain':
const chain = (req, res) => {
const response = {
chain: blockchain.chain,
length: blockchain.chain.length
};
return res.end(JSON.stringify(response));
}
chain(req, res);
break;

Of course, when the application is first started up, it only returns the genesis block:

{
"chain": [
{
"index": 1,
"timestamp": 1525082818630,
"proof": 1,
"previousHash": 1
}
],
"length": 1
}

Now we can start to mine new blocks.

/mine

This endpoint goes through everything that we have discussed so far with regards to mining a new block and using the PoW algorithm.

case '/mine':
const mine = (req, res) => {
const lastBlock = blockchain.lastBlock();
const lastProof = lastBlock.proof;
const proof = blockchain.proofOfWork(lastProof);
const previousHash = blockchain.hash(JSON.stringify(lastBlock));
const block = blockchain.createBlock(proof, previousHash);

const response = {
message: 'New block forged',
index: block.index,
proof: block.proof,
previousHash: block.previousHash
};

res.end(JSON.stringify(response));
};
mine(req, res);
break;

Here's how the response looks like:

{
"message": "New block forged",
"index": 2,
"proof": 16562,
"previousHash": "ca6b685a21712d187fbbb2d0bf5745d617fe275f20524c7bad0c643c25e6f33f"
}

It is possible to mine multiple blocks - and once they are mined, a call to the /chain endpoint will show all the mined blocks:

{
"chain": [
{
"index": 1,
"timestamp": 1525083443166,
"proof": 1,
"previousHash": 1
},
{
"index": 2,
"timestamp": 1525083449558,
"proof": 16562,
"previousHash": "2486175ba172fa43712ad062275c7dc03a179a5582ecbfaf3276bbd14929732b"
},
{
"index": 3,
"timestamp": 1525083449838,
"proof": 37046,
"previousHash": "1ae143cbd8dfe7adfd6bc217bb37f48e99f97c4bc1d2c2d557e5b0c3a9619eb3"
},
{
"index": 4,
"timestamp": 1525083450248,
"proof": 21181,
"previousHash": "869f55314061dae9da72fb85d0020833016198a7ab489bf8bd689e1d723d11da"
}
],
"length": 4
}

Conclusion

I hope this article was useful for you to understand the basics of Blockchain - it helped me to grasp the basic concepts surrounding this technology. A few things are missing from this implementation, for example, there are no data available in the chains, nor it's possible to do transactions (think about how BitCoins are sold and purchased - those are done via transactions). These features could be easily implemented though, feel free to have a go at it!