Building A Blockchain In .NET Core - Proof Of Work

Introduction

In my previous article, Building a Basic Blockchain In .NET Core, I created a basic blockchain that was used to store transactions. The basic blockchain can be tampered with easily. Even though there was a validation mechanism, by updating a block and re-calculating hashes of all blocks after it, the tampered-with blockchain passed validation. The situation could become worse in the real world because re-calculation can be done in a short period of time with a modern computer. We must come up with a solution to stop attackers from tampering with a blockchain. The solution actually is pretty simple and straightforward. That is Proof of Work.

What is Proof of Work?

Proof of Work is a scheme used in generating a new block for a blockchain. It requires a significant amount of work, a.k.a. computing time, to generate a piece of information for a new block. The generated information must be simple and verifiable. Therefore, it can be easily verified by any nodes in the network. The generated information is the proof that this block is valid and some work has been done to generate it. The amount of work required for generating a new block is determined from the average computing time on generating a new block in the entire Blockchain network. The algorithm picked to implement Proof of Work scheme is a hash algorithm. The most widely used hash algorithm in Proof of Work is SHA-256. To generate a hash that has a specific format, like a number of leading zeros, is very computing intensive and time-consuming. There is no shortcut for the generating process. However, verifying the source data that matches with the generated hash is trivial. Because a specific piece of data can only get a specific hash, the source data must be changed to generate a different hash. This is solved by introducing “nonce” in the data structure. The nonce is an integer. By increasing the nonce, the hash algorithm can generate a different hash. This process will be ended until the generated hash meets the requirement, we call it difficulty.

Mining

The implementation of Proof of Work is called mining. Just like coal mining. Mining is a process of finding something valuable with a lot of effort. In Blockchain, mining is to find the right hash with a lot of computing time.

Implementation

In order to add proof-of-work to the basic blockchain, we need to update Block and Blockchain classes.

Block

A new property, nonce, is added into the Block class.

  1. public int Nonce { get; set; } = 0;  
Blockchain

CalculateHash method is updated to include nonce in hash generating.

  1. public string CalculateHash()  
  2. {  
  3.     SHA256 sha256 = SHA256.Create();  
  4.   
  5.     byte[] inputBytes = Encoding.ASCII.GetBytes($"{TimeStamp}-{PreviousHash ?? ""}-{Data}-{Nonce}");  
  6.     byte[] outputBytes = sha256.ComputeHash(inputBytes);  
  7.   
  8.     return Convert.ToBase64String(outputBytes);  
  9. }  

In the end, a new method, Mine, is added to accept difficulty as a parameter. The difficulty is an integer that indicates the number of leading zeros required for a generated hash. The Mine method tries to find a hash that matches with difficulty. If a generated hash doesn’t meet the difficulty, then it increases nonce to generate a new one. The process will be ended when a qualified hash is found.

  1. public void Mine(int difficulty)  
  2. {  
  3.     var leadingZeros = new string('0', difficulty);  
  4.     while (this.Hash == null || this.Hash.Substring(0, difficulty) != leadingZeros)  
  5.     {  
  6.         this.Nonce++;  
  7.         this.Hash = this.CalculateHash();  
  8.     }  
  9. }  

Blockchain

The Blockchain class is updated to have a new field Difficulty.

  1. public int Difficulty { set; get; } = 2;  

The new block generating process is updated to add a mining step.

  1. public void AddBlock(Block block)  
  2. {  
  3.     Block latestBlock = GetLatestBlock();  
  4.     block.Index = latestBlock.Index + 1;  
  5.     block.PreviousHash = latestBlock.Hash;  
  6.     block.Mine(this.Difficulty);  
  7.     Chain.Add(block);  
  8. }  

Program

After updating Block and Blockchain class, some debugging code is added to the program to result in the amount of time spent on added blocks.

  1. var startTime = DateTime.Now;  
  2.   
  3. Blockchain phillyCoin = new Blockchain();  
  4. phillyCoin.AddBlock(new Block(DateTime.Now, null"{sender:Henry,receiver:MaHesh,amount:10}"));  
  5. phillyCoin.AddBlock(new Block(DateTime.Now, null"{sender:MaHesh,receiver:Henry,amount:5}"));  
  6. phillyCoin.AddBlock(new Block(DateTime.Now, null"{sender:Mahesh,receiver:Henry,amount:5}"));  
  7.   
  8. var endTime = DateTime.Now;  
  9.   
  10. Console.WriteLine($"Duration: {endTime - startTime}");  

The adding a new block process looks like below now,

Blockchain

Summary

Proof-of-Work requires a significant amount of computing time to generate new blocks. By slowing down the new block generating process and average out the block adding time in a Blockchain network, tampering with a blockchain becomes virtually impossible. Attackers need a lot of computer power to tamper with a blockchain because they need to update all blocks in the chain after the tampered block and catch up with newly added blocks. This is very difficult because new blocks are constantly added into the blockchain by good players. Proof-of-Work requires a lot of computing time. This brings up a question, why are people willing to connect their computer to a blockchain network and donate their computing time to the mining process?

This will be answered in my next article.