Tuesday, April 16, 2024

Implementing the Blockchain in JavaScript

Blockchain looks like a technology from another world and it raises many doubts. Nevertheless, it is extremely easy to define:

  1. Blockchain is nothing more than an open and distributed database.
  2. This database is made up of Blocks. And all these Blocks are linked together in a sequence. Hence the name Blockchain.
  3. Also, this database is immutable, and makes sense to be like that.

Imagine if it were possible for someone, intentionally, to modify your account. Now the 10 Bitcoins you own, have become one.

Let’s begin the implementation by the easy part: The Blocks. The structure of a Block must have the following fields:

  • Index
  • Timestamp
  • Hash
  • PreviousHash
  • Data

Index and timestamp are common fields in virtually all databases. The data field is mainly for saving transactions, but we can also put other information. The hash is calculated internally and serves to maintain the integrity of each Block and the security of the entire system (as we will see at the end of the article). And, lastly, the previousHash, the link that connects each Block to its previous Block.

With this, we have the first implementation of a Block:

const sha256 = require('crypto-js/sha256')
 
class Block {
  constructor(index = 0, previousHash = null, data = 'Genesis block') {
    this.index = index
 
    this.previousHash = previousHash
 
    this.data = data
 
    this.timestamp = new Date()
 
    this.hash = this.generateHash()
  }
 
  generateHash() {
    return sha256(this.index + this.previousHash + JSON.stringify(this.data) + this.timestamp).toString()
  }
}
 
module.exports = Block

The generateHash function uses the crypto-js external library to generate the hash following the sha256 standard.

It sounds complicated, but all you need to know is that this function will receive a string, for example: “abc.”

Blockchain

Now that we have a minimal version of a Block, we can start building the Blockchain, which – as I said before – is a sequence of Blocks linked together.

With this, this is the first version of our Blockchain:

const Block = require('./block')
 
class Blockchain {
  constructor() {
    this.blocks = [new Block()]
    this.index = 1
  }
 
  getLastBlock() {
    return this.blocks[this.blocks.length - 1]
  }
 
  addBlock(data) {
    const index = this.index
    const previousHash = this.getLastBlock().hash
 
    const block = new Block(index, previousHash, data)
 
    this.index++
    this.blocks.push(block)
  }
}
 
module.exports = Blockchain

In the class constructor, we have the Blocks array already initialized with the Genesis Block (the first Block created in the Bitcoin registry). I also added the index to be able to increment each time a new Block is added in Blockchain.

In addition, two functions have been created: getLastBlock and addBlock.

The first is extremely simple, it serves to pick up the last Block that was created. The second one is a bit more complicated, but it’s also nothing out of this world. It serves to add new Blocks to Blockchain.

Integrity

Although the previous version already works reasonably well, we need to add some assurance that Blockchain has not been tampered with by a malicious attack.

We need to add a new function to check the integrity of our Blockchain:

isValid() {
  for (let i = 1; i < this.blocks.length; i++) {
    const currentBlock = this.blocks[i]
    const previousBlock = this.blocks[i - 1]
 
    if (currentBlock.hash !== currentBlock.generateHash()) {
      return false
    }
 
    if (currentBlock.index !== previousBlock.index + 1) {
      return false
    }
 
    if (currentBlock.previousHash !== previousBlock.hash) {
      return false
    }
  }
  return true
}

Remembering that to verify the integrity of Blockchain, we need to guarantee three characteristics:

  1. If the hash of each block was generated correctly;
  2. If the Blocks indexes are in sequence;
  3. If the Blocks are linked together through the hashes.

With this simple function we can test if the malicious modifications were made and if Blockchain should be invalidated:

const Blockchain = require('./blockchain')
 
const blockchain = new Blockchain()
blockchain.addBlock({ amount: 4 })
blockchain.addBlock({ amount: 50 })
 
console.log(blockchain.isValid()) // true
blockchain.blocks[1].data.amount = 30000 // ataque malicioso
console.log(blockchain.isValid()) // false

And finally, we have the very first basic and functional version of the Blockchain in JavaScript:

const Block = require('./block')
 
class Blockchain {
  constructor() {
    this.blocks = [new Block()]
    this.index = 1
  }
 
  getLastBlock() {
    return this.blocks[this.blocks.length - 1]
  }
 
  addBlock(data) {
    const index = this.index
    const previousHash = this.getLastBlock().hash
 
    const block = new Block(index, previousHash, data)
 
    this.index++
    this.blocks.push(block)
  }
 
  isValid() {
    for (let i = 1; i < this.blocks.length; i++) {
      const currentBlock = this.blocks[i]
      const previousBlock = this.blocks[i - 1]
 
      if (currentBlock.hash !== currentBlock.generateHash()) {
        return false
      }
 
      if (currentBlock.index !== previousBlock.index + 1) {
        return false
      }
 
      if (currentBlock.previousHash !== previousBlock.hash) {
        return false
      }
    }
    return true
  }
}
 
module.exports = Blockchain

Problems

Although we already have an initial working version, we can still greatly improve our implementation.

One of the problems with this current version is that users can create new Blocks very quickly, which can invalidate the Blockchain, or even worse. I will not go into many details of this particularity about the technology, but this article does an excellent job in that regard.

In short, we need some tool that does not allow users to create Blocks wildly.

Proof of Work

The concept of Proof of Work, as the name itself suggests, is a mechanism that causes a user to have meaningful computational work in performing a given task.

This tool was already used before Bitcoin was invented to prevent, for example, spam and DoS attacks.

In Bitcoin, Proof of Work works as follows: the hash that is automatically generated in each Block must begin with an amount X of zeros, depending on the overall difficulty of the system.

For example, if the overall system difficulty is 1, this hash is invalid because it does not begin with a zero:

b5036427617139d3ad9bf650d74ae43710e36d4f63829b92b807da37c5d38e8a

However, this another hash is valid because starts with a zero:

01da8bff6cfea68a3f0a5bafc9b24d07f503e2282db36ffb58d43f9f4857c54a

Whenever a user creates a new Block, it will need to create multiple hashes, until one of them has the number of zeros at the beginning, so that the general rule of the system is met.

Recalling that the higher the number of zeros that must be at the beginning of the hash, the greater the computational power required for the task.

That said, we will now implement the final version of Blockchain with Mining.

First, let’s change the Blocks:

class Block {
  constructor(index = 0, previousHash = null, data = 'Genesis block', difficulty = 1) {
    this.index = index
    this.previousHash = previousHash
    this.data = data
    this.timestamp = new Date()
    this.difficulty = difficulty
    this.nonce = 0
 
    this.mine()
  }
 
  /* */
}

In the constructor, we add the difficulty (general system difficulty) and nonce (number of retries until the correct hash is created). Also, we have a call to the mine function:

mine() {
  this.hash = this.generateHash()
 
  while (!(/^0*$/.test(this.hash.substring(0, this.difficulty)))) {
    this.nonce++
    this.hash = this.generateHash()
  }
}

The mine function will create hashes until the number of zeros to the left of the hash is acknowledged.

Remember that in order for the created hashes to be different, we must add the nonce field in the generateHash,/em>:

generateHash() {
  return sha256(this.index + this.previousHash + JSON.stringify(this.data) + this.timestamp + this.nonce).toString()
}

With that, we have the final version of our Blocks with mining:

const sha256 = require('crypto-js/sha256')
 
class Block {
  constructor(index = 0, previousHash = null, data = 'Genesis block', difficulty = 1) {
    this.index = index
    this.previousHash = previousHash
    this.data = data
    this.timestamp = new Date()
    this.difficulty = difficulty
    this.nonce = 0
 
    this.mine()
  }
 
  generateHash() {
    return sha256(this.index + this.previousHash + JSON.stringify(this.data) + this.timestamp + this.nonce).toString()
  }
 
  mine() {
    this.hash = this.generateHash()
 
    while (!(/^0*$/.test(this.hash.substring(0, this.difficulty)))) {
      this.nonce++
      this.hash = this.generateHash()
    }
  }
}
 
module.exports = Block

Now, we only need to alter the Blockchain for the field “difficulty” to be passed to the other Blocks:

const Block = require('./block')
 
class Blockchain {
  constructor(difficulty = 1) {
    this.blocks = [new Block()]
    this.index = 1
    this.difficulty = difficulty
  }
 
  getLastBlock() {
    return this.blocks[this.blocks.length - 1]
  }
 
  addBlock(data) {
    const index = this.index
    const difficulty = this.difficulty
    const previousHash = this.getLastBlock().hash
 
    const block = new Block(index, previousHash, data, difficulty)
 
    this.index++
    this.blocks.push(block)
  }
 
  isValid() {
    for (let i = 1; i < this.blocks.length; i++) {
      const currentBlock = this.blocks[i]
      const previousBlock = this.blocks[i - 1]
 
      if (currentBlock.hash !== currentBlock.generateHash()) {
        return false
      }
 
      if (currentBlock.index !== previousBlock.index + 1) {
        return false
      }
 
      if (currentBlock.previousHash !== previousBlock.hash) {
        return false
      }
    }
    return true
  }
}
 
module.exports = Blockchain

Conclusion

This is only a small demonstration of all the power crypto libraries, specially the JavaScript based ones, can do for you. Of course, it’s a very restricted field of the market, but it’s also growing every day and, soon, it’ll get a special place in company’s realities.

About the Author

Diogo Souza works as a Java Developer at PagSeguro and has worked for companies such as Indra Company, Atlantic Institute and Ebix LA. He is also an Android trainer, speaker at events on Java and mobile world.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured