Build a Tiny Blockchain with Nodejs - Chapter 3

4 minute read

In chapter 2 we’ve implemented proof-of-work to protect the blockchains from spammers and attackers. Blockchain can only store one transaction in a block and there are no rewards for miners.

Block class

Currently Block class has index, timestamp, data, previousHash, hash and nonce properties. And we are going to rename data to transactions in order to store multiple transactions.

constructor(index, transactions, previousHash = '') {
  this.index = index;
  this.timestamp = Math.floor(Date.now() / 1000);
  this.transactions = transactions;
  this.previousHash = previousHash;
  this.hash = this.calculateHash();
  this.nonce = 0;
}

Once we rename data to transactions in Block class, we also have to rename its in calculateHash() function as well.

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

Transaction class

Block is able to store multiple transactions. We are going to define a Transaction class so we can lock down what properties a transaction should have:

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

In Transaction class consist of a sender (fromAddress), a receiver (toAddress) and an amount. If required, you can add more fields to a transaction.

Modify Blockchain

We are going to make the Blockchain work with all these new changes. The first thing that we need is a place to store pending transactions.

As you know blockchains create blocks on a steady interval using the proof-of-work algorithm. In the case of Bitcoin, the difficulty is adjusted so that new blocks are created roughly every 10 minutes. However it should be possible to submit new transactions in between the creation of two blocks.

To do this we are going to update Blockchain constructor so it has a place to store pending transactions. We are also going to create a property that defines how much coins a miner gets as a reward:

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

    // Property to store transactions in between block creation
    this.pendingTransactions = [];

    // How many coins a miner will get as a reward for his efforts
    this.miningReward = 100;
  }
}

We won’t allow people adding blocks to our chain directly anymore. Instead they have to add transactions who will be included in the next block. So we’ll replace the addBlock() method with createTransaction():

createTransaction(transaction) {
  // Push into onto the "pendingTransactions" array
  this.pendingTransactions.push(transaction);
}

Mine Blocks

People can now add new transactions to the list of pending. But another way, we need to clear those out and put them inside actual blocks. So to do that let’s create a minePendingTransactions() method. This method won’t only mine a new block with all the pending transactions, it will also send a mining reward to the miner.

minePendingTransactions(miningRewardAddress) {
  // Create new block with all pending transactions and mine it.
  let block = new Block(0, this.pendingTransactions, this.getLatestBlock().hash);
  block.mineBlock(this.difficulty);

  // Add the newly mined block to the chain
  this.chain.push(block);

  // Reset the pending transactions and send the mining reward
  this.pendingTransactions = [
    new Transaction(null, miningRewardAddress, this.miningReward)
  ];
}

Note that the method takes an argument miningRewardAddress. If we start mining, we can pass along your wallet address to this method. Once we successfully mined a block, the system will create a new transaction to give us mining reward (in this case 100 coins).

We’ve take all the pending transactions and add them to a block. In reality however that won’t work because the size of a block is limited. In the case of Bitcoin, the size of a block is limited 20mb. If there are more transactions that can fit in a block, the miner gets to choose which transaction is included and which is not (usually the ones with the highest fee wins).

Balance of an Address

It would be nice to be able to check the balances of the addresses on the blockchain:

getBalanceOfAddress(address) {
  let balance = 0; // we start at zero!

  // Loop over each block and each transaction inside the block
  for(const block of this.chain) {
    for(const trans of block.transactions) {
      // If the given address is the sender -> reduce the balance
      if(trans.fromAddress === address) {
        balance -= trans.amount;
      }

      // If the given address is the receiver -> increase the balance
      if(trans.toAddress === address) {
        balance += trans.amount;
      }
    }
  }

  return balance;
}

Testing

Okay, let’s create some transactions:

let myCoin = new Blockchain();

console.log('Creating some transactions...');
myCoin.createTransaction(new Transaction('address1', 'address2', 100));
myCoin.createTransaction(new Transaction('address2', 'address1', 50));

These above transactions are now pending, in order to get confirmed, we have to start the miner:

console.log('Starting the miner...');
savjeeCoin.minePendingTransactions('my-address');

When we start the miner, we also pass along an address on which we want to receive the mining reward. In this case, my address is my-address.

Okay, let’s check the balance of my-address:

console.log('Balance of my address is', savjeeCoin.getBalanceOfAddress('my-address'));
// Output: 0

The resulting is my balance is zero. Why? Well if we look closely at the code, we’ll see that the system creates a new block and then adds our mining rewards as a new pending transaction. That transaction will be included in the next block. So if we start the miner again, we will receive our 100 coin reward.

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

console.log('Balance of my address is', savjeeCoin.getBalanceOfAddress('my-address'));
// Output: 100

You can download the source code and try it out on Github.

Categories:

Updated: