Writing a Tiny Blockchain in JavaScript

Author: Savjee. Be

Translator: JeLewine

Nearly everyone has heard of cryptocurrencies like Bitcoin and Ether, but very few understand the technology behind them. In this blog post, I will use JavaScript to create a simple blockchain to demonstrate how their insides actually work. I’ll call it SavjeeCoin!

The paper is divided into three parts:

  1. Part1: Implement a basic blockchain
  2. Part2: Implement POW
  3. Part3: Rewards for trading and mining

Part1: Implement a basic blockchain

Block chain

Blockchain is a public database of blocks that anyone can access. This doesn’t seem like anything special, but they have an interesting property: they’re immutable. Once a block is added to the blockchain, it cannot be changed unless the remaining blocks are invalidated.

That’s why cryptocurrencies are based on blockchains. You don’t want people changing trades after they’ve been done!

Create a block

Blockchain is made up of lots and lots of blocks linked together (which sounds fine…). . The blocks on the chain somehow allow us to detect if someone has manipulated any of the previous blocks.

So how do we ensure data integrity? Each block contains a hash calculated based on its contents. It also contains the hash of the previous block.

Here’s what a block class might look like when written in JavaScript:

const SHA256 = require("crypto-js/sha256");
class Block {
  constructor(index, timestamp, data, previousHash = ' ') {
    this.index = index;
    this.previousHash = previousHash;
    this.timestamp = timestamp;
    this.data = data;
    this.hash = this.calculateHash();
  }

  calculateHash() {
    returnSHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data)).toString(); }}Copy the code

Since SHA256 is not supported in JavaScript, I introduced the Crypto-JS library. I then define a constructor to initialize the properties of my block. Each block is assigned an index attribute to tell us where it is on the chain. We also generate a timestamp and some data that needs to be stored in the block. Finally, the hash of the previous block.

Create a chain

Now we can link blocks together in the Blockchain class! Here is the code implemented in JavaScript:

class Blockchain{
  constructor() {
    this.chain = [this.createGenesisBlock()];
  }

  createGenesisBlock() {
    return new Block(0, "01/01/2017"."Genesis block"."0");
  }

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

  addBlock(newBlock) {
    newBlock.previousHash = this.getLatestBlock().hash;
    newBlock.hash = newBlock.calculateHash();
    this.chain.push(newBlock);
  }

  isChainValid() {
    for (let i = 1; i < this.chain.length; i++){
      const currentBlock = this.chain[i];
      const previousBlock = this.chain[i - 1];

      if(currentBlock.hash ! == currentBlock.calculateHash()) {return false;
      }

      if(currentBlock.previousHash ! == previousBlock.hash) {return false; }}return true; }}Copy the code

In the constructor, I initialize the entire chain by creating an array containing the creation block. The first block is special because it cannot point to the previous block. I also added the following two methods:

  • getLatestBlock()Returns the latest block on our blockchain.
  • addBlock()Responsible for adding new blocks to our chain. To do this, we add the hash from the previous block to our new block. So we can keep the whole chain intact. Because whenever we change the contents of the latest block, we need to recalculate its hash. When the calculation is complete, I will push the block into the chain (an array).

Finally, I create an isChainValid() to ensure that no one has tampered with the blockchain. It traverses all the blocks to check that each block has the correct hash. It checks that each block points to the correct previous block by comparing previousHash. It will return true if everything is fine otherwise it will return false.

Using blockchain

Now that our blockchain class is complete, we can actually start using it!

let savjeeCoin = new Blockchain();
savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 }));
savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));
Copy the code

Here I just created an instance of the blockchain and named it SavjeeCoin! Then I added some blocks to the chain. The block can contain any data you want, but in the code above I chose to add an object with the amount attribute.

Try it!

In my introduction I said that blockchain is immutable. Once added, blocks cannot be changed. Let’s try it!

// Check if it is valid (will returntrue)
console.log('Blockchain valid? '+ savjeeCoin.isChainValid()); Savjeecoin.chain [1]. Data = {amount: 100}; // Check again to see if it is valid (will returnfalse)
console.log("Blockchain valid? " + savjeeCoin.isChainValid());
Copy the code

I will initially verify the integrity of the chain by running isChainValid(). We have operated on any block, so it will return true.

I then changed the data for the first block (index 1) on the chain. I then double-checked the integrity of the entire chain and found that it returned false. Our entire chain no longer works.

conclusion

The little chestnut is far from finished. It has not implemented POW or P2P networks to communicate with other miners.

But he did prove how blockchain works. Many people thought the principle would be very complicated, but this article proves that the basic concepts of blockchain are very easy to understand and implement.

Part2: Proof-of-work (proof-of-work)

In part1 we created a simple blockchain using JavaScript to demonstrate how blockchain works. The implementation was incomplete, however, and many people found it still possible to tamper with the system. That’s right! Our blockchain needs another mechanism to defend against attacks. So let’s see how we can do this!

The problem

Now we can create blocks very quickly and add them to our blockchain very quickly. But this leads to three problems:

  • One: people can quickly create blocks and stuff our chains with junk. A large number of blocks would overload our blockchain and render it unusable.
  • Second: because it is so easy to create a valid block, one can tamper with one block in the chain and then recalculate the hash of all the blocks. Even if they have tampered with the block, they can still end up with a valid block.
  • Third: You can effectively control the blockchain by combining these two vulnerabilities. Blockchains are powered by P2P networks in which nodes add blocks to the longest available chain. So you can tamper with blocks, then count all the other blocks, and finally add as many blocks as you want. You end up with the longest chain, and all the other nodes accept it and add their own blocks.

Clearly we need a solution to these problems: POW.

What is a POW

POW is a mechanism that existed before the first blockchain was created. This is a simple technique that does a certain number of calculations to prevent abuse. Workload is key to preventing garbage padding and tampering. If it requires a lot of computational power, it’s no longer worth it to fill it with garbage.

Bitcoin implements POW by requiring hash to be a specific number of zeros. This is also called difficulty

But wait! How can a block hash be changed? In the case of Bitcoin, a block contains information about various financial transactions. We certainly don’t want to mess with that data in order to get the right hash.

To solve this problem, the blockchain adds a nonce value. The Nonce is the number of times a valid Hash is searched. Also, because you can’t predict the output of a hash function, you have to try a lot of combinations before you get a hash that satisfies the difficulty condition. Finding a valid hash (creating a new block) is called mining in circles.

In the case of Bitcoin, POW ensures that only one block can be added every 10 minutes. As you can imagine, it takes a lot of computing power for garbage fillers to create a new block, and it’s hard for them to fool the network, let alone tamper with the entire chain.

Implement POW

How do we do that? Let’s start by modifying our block class and adding the Nonce variable to its constructor. I’m going to initialize it and set its value to 0.

constructor(index, timestamp, data, previousHash = ' ') {
  this.index = index;
  this.previousHash = previousHash;
  this.timestamp = timestamp;
  this.data = data;
  this.hash = this.calculateHash();
  this.nonce = 0;
}
Copy the code

We also need a new way to increase the Nonce until we get a valid hash. Again, it depends on the difficulty. So we get the difficulty as a parameter.

mineBlock(difficulty) {
    while(this.hash.substring(0, difficulty) ! == Array(difficulty + 1).join("0")) {
        this.nonce++;
        this.hash = this.calculateHash();
    }
    console.log("BLOCK MINED: " + this.hash);
}
Copy the code

Finally, we need to change the calculateHash() function. Because he doesn’t currently use Nonce to compute hashes.

calculateHash() {
  return SHA256(this.index +
    this.previousHash +
    this.timestamp +
    JSON.stringify(this.data) +
    this.nonce
  ).toString();
}
Copy the code

Put them together and you get a block class like this:

class Block {
  constructor(index, timestamp, data, previousHash = ' ') {
    this.index = index;
    this.previousHash = previousHash;
    this.timestamp = timestamp;
    this.data = data;
    this.hash = this.calculateHash();
    this.nonce = 0;
  }

  calculateHash() {
    return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data) + this.nonce).toString();
  }

  mineBlock(difficulty) {
    while(this.hash.substring(0, difficulty) ! == Array(difficulty + 1).join("0")) {
      this.nonce++;
      this.hash = this.calculateHash();
    }
    console.log("BLOCK MINED: "+ this.hash); }}Copy the code

Modifying the blockchain

Now that our block has Nonce and can be mined, we need to make sure our blockchain supports this new behavior. Let’s start by adding a new attribute to the blockchain to track the difficulty of the entire chain. I’ll set it to 2 (which means the block hash must start with 2 zeros).

constructor() {
  this.chain = [this.createGenesisBlock()];
  this.difficulty = 2;
}
Copy the code

All that remains now is to change the addBlock() method to ensure that the block is actually mined before adding it to the chain. Next we pass the difficulty to the block.

addBlock(newBlock) {
  newBlock.previousHash = this.getLatestBlock().hash;
  newBlock.mineBlock(this.difficulty);
  this.chain.push(newBlock);
}
Copy the code

And you’re done! Our blockchain now has a POW to defend against attacks.

test

Now let’s test our blockchain to see what happens when we add a new block under POW. I’m going to use the previous code. We will create a new blockchain instance and add two blocks to it.

let savjeeCoin = new Blockchain();

console.log('Mining block 1');
savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 }));

console.log('Mining block 2');
savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));
Copy the code

If you run the above code, you’ll see that adding new blocks is still pretty fast. This is because the difficulty is currently 2 (or your computer is very good).

If you create a blockchain instance of difficulty 5, you will find that your computer will spend about 10 seconds mining. As the difficulty increases, your defenses become more protected against attacks.

disclaimer

As said before: this is by no means a complete blockchain. It still lacks many features (like P2P networks). This is just to illustrate how blockchain works.

And: Mining in JavaScript is not fast because of single threads.

Part3 Trading and mining rewards

In the first two sections we created a simple blockchain and added POW to defend against attacks. However, we also got lazy along the way: our blockchain can only store one transaction per block, and there is no reward for miners. Now, let’s solve this problem!

Refactoring block class

Now a block with the index, previousHash, timestamp, the data, the hash and nonce attributes. The index property isn’t very useful, in fact I don’t even know why I added it in the first place. So I removed it and renamed Data transactions to be more semantic.

class Block{
  constructor(timestamp, transactions, previousHash = ' ') { this.previousHash = previousHash; this.timestamp = timestamp; this.transactions = transactions; this.hash = this.calculateHash(); this.nonce = 0; }}Copy the code

When we change the block class, we also have to change the calculateHash() function. It still uses the old index and data attributes.

calculateHash() {
  return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.transactions) + this.nonce).toString();
}
Copy the code

transactional

Within blocks, we will be able to store multiple transactions. So we also need to define a transaction class where we can lock down the attributes that the transaction should have:

class Transaction{ constructor(fromAddress, toAddress, amount){ this.fromAddress = fromAddress; this.toAddress = toAddress; this.amount = amount; }}Copy the code

This transaction example is very simple and contains only the originator (fromAddress) and receiver (toAddress) and the quantity. You can add more fields if you want, but this is for minimal implementation.

Tweaking our blockchain

The biggest task at hand: adapting our blockchain to accommodate these new changes. The first thing we need is a place to store pending transactions.

As you know, due to POW, blockchains can create blocks stably. In the Bitcoin scenario, the difficulty is set to create a new block roughly every 10 minutes. However, it is possible to submit new transactions between the creation of two blocks.

To do this, we first need to change the constructor of our blockchain so that it can store pending transactions. We’ll also create a new attribute that defines how much money a miner gets as a reward:

class Blockchain{
  constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 5; // Where transactions are stored between block generation this.PendingTransactions = []; // This. MiningReward = 100; }}Copy the code

Next, we’ll tweak our addBlock() method. But by adjustment I mean delete and rewrite it! We will no longer allow people to add blocks directly to the chain. Instead, they must add the transaction to the next block. And we rename addBlock() to createTransaction(), which looks more semantic:

CreateTransaction (transaction) {// There should be some validation here! / / this. Push the pending transactions array pendingTransactions. Push (transaction); }Copy the code

dig

People can now add new transactions to the list of pending transactions. But anyway, we need to clean them up and move them into the actual block. To do this, let’s create a minePendingTransactions() method. This method not only unearths all the new blocks that need to be traded, but also sends a reward to the miner.

MinePendingTransactions (miningRewardAddress) {// Create a new block with all pending transactions and dig..letblock = new Block(Date.now(), this.pendingTransactions); block.mineBlock(this.difficulty); // Add the new mine to the chain this.chain.push(block); // reset the list of pendingTransactions and send rewards this.pendingTransactions = [new Transaction(null, miningRewardAddress, this.miningreward)]; }Copy the code

Notice that this method takes the parameter miningRewardAddress. If you start mining, you can pass your wallet address to this method. Once a mine is successfully mined, the system creates a new transaction to give you a mine reward (100 coins in this case).

It is important to note that in this chestnut, we add all pending transactions together into one block. In practice, however, this doesn’t work because the block size is limited. In Bitcoin, a block is about 2Mb in size. If more deals can be squeezed into a block, miners can choose which deals are done and which are not (usually the more expensive ones win).

Address balance

After testing our code money let’s do one more thing! It would be nice to be able to check the balance of addresses on our blockchain.

getBalanceOfAddress(address){
  letbalance = 0; // you start at zero! // Iterate over each block and the transactions within each blockfor(const block of this.chain){
    for(const trans of block.transactions){// If the address is the initiator -> reduce the balanceif(trans.fromAddress === address){ balance -= trans.amount; } // If the address is the receiver -> increase the balanceif(trans.toAddress === address){ balance += trans.amount; }}}return balance;
}
Copy the code

test

Well, we’re done and can finally see if everything works! To do this, we created some transactions:

let savjeeCoin = new Blockchain();

console.log('Creating some transactions... ');
savjeeCoin.createTransaction(new Transaction('address1'.'address2', 100));
savjeeCoin.createTransaction(new Transaction('address2'.'address1', 50));
Copy the code

These deals are on hold and in order for them to be confirmed we have to start mining:

console.log('Starting the miner... ');
savjeeCoin.minePendingTransactions('xaviers-address');
Copy the code

When we start mining, we also pass in an address where we want to get a mining reward. In this case, my address is Xaviers-address (very complicated!) .

After that, let’s check the account balance for Xaviers-address:

console.log('Balance of Xaviers address is', savjeeCoin.getBalanceOfAddress('xaviers-address')); // Output: 0Copy the code

My account output is 0? ! Wait, why? Don’t I deserve my mining reward? Well, if you look closely at the code, you’ll see that the system creates a transaction and then adds your mining reward as a new transaction to be processed. This transaction will be included in the next block. So if we start mining again, we will receive our reward of 100 coins!

console.log('Starting the miner again! ');
savjeeCoin.minePendingTransactions("xaviers-address");

console.log('Balance of Xaviers address is', savjeeCoin.getBalanceOfAddress('xaviers-address')); // Output: 100Copy the code

Limitations and Conclusions

Now we have a blockchain that can store multiple transactions on a block, and it’s rewarding for miners.

However, there are still some disadvantages: sending currency is that we do not check whether the originator has enough balance to actually carry out the transaction. However, this is actually an easy thing to fix. We also did not create a new wallet and signature transaction (traditionally done with public/private key encryption).

Disclaimer & source code

I want to point out that this is by no means a complete blockchain implementation! It still lacks a lot of features. This is just to validate a few concepts to help you understand how blockchain works.

The source code for the project is on my GitHub