Tạo ứng dụng blockchain đầu tiên với C#

thaotrinh.info__tao-ung-dung-blockchain-dau-tien-voi-c

Thông qua bài viết này mình sẽ hướng dẫn các bạn làm quen và hiểu thêm về công nghệ Blockchain.

Mục đích

– Tạo hệ thống Blockchain đơn giản nhất.
– Tạo hệ thống mining (poor of work) đơn giản.
– Khám phá thêm về Blockchain.

Kiến thức cần chuẩn bị: OOP, biết 1 ngôn ngữ hướng đội tượng.
Ok giờ xắn tay vào việc nào

Making the Blockchain.

Một blockchain là danh sách, chuỗi các khối (A blockchain is just a chain/list of blocks). Mỗi block trong blockchain sẽ chứa chữ ký số của nó, chữ ký số của khối trước nó và dữ liệu của khối (dữ liệu giao dịch là một ví dụ).

thaotrinh.info__tao-ung-dung-blockchain-dau-tien-voi-c

 

Hash = Digital Signature.

Mỗi khối không chỉ chứa mã hash của khối trước nó mà nó còn chứa mã hash của chính nó, được tính toán dựa trên mã hash của khối trước nó. Nếu dữ liệu khối trước đó bị thay đổi (khối A), việc này dẫn tới thay đổi mã hash của khối A, do mã hash được tính toán dựa trên data này. Việc này dẫn tới sự thay đổi mã hash của tất cả các khối trong chuỗi. Việc tính toán và so sánh giúp chúng ta phát hiện ra bất kì sự thay đổi nào trong blockchain.

Điều này có nghĩa là gì? – Nghĩa là khi thay đổi dữ liệu của 1 block thì sẽ dẫn tới thay đổi dữ liệu của rất rất nhiều block, và dãn tới thay đổi chain (break the chain).

Giờ chúng ta sẽ thừ bắt đầu xây dựng một hệ thống Blockchain đơn giản.

Tạo project đặt tên là NoobChain

Thêm class Block để xây dựng blockchain

class Block
{
        public String hash;
        public String previousHash;
        private String data; // Trong ví dụ này chúng ta chỉ lưu data là một thông báo.
        private long timeStamp;
        

        //Block Constructor.
        public Block(String data, String previousHash)
        {
            this.data = data;
            this.previousHash = previousHash;
            this.timeStamp = DatetimeHandle.GetTime();            
        }     

}

Như bạn thấy Block cơ bản của chúng ta chứa một chuỗi hash sẽ dùng để lưu chữ kí số. Một biến previousHash để lưu hash của khối trước nó, và dữ liệu của khối.

Bước tiếp theo chúng ta sẽ tiến hành tạo chữ ký số.
Tạo lớp HashSha256, áp dụng thuật toán sha256 để sinh chuỗi, với nội dung như sau:

class HashSha256
{
  public override string Hash(string strInput)
  {
    try
    {
      var crypt = new System.Security.Cryptography.SHA256Managed();
      var hash = new System.Text.StringBuilder();
      byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(strInput));
      foreach (byte theByte in crypto)
      {
        hash.Append(theByte.ToString("x2"));
      }
      return hash.ToString();
    }
    catch (Exception e)
    {
      throw e;
    }    
  }
}

Sau đó quay trờ lại lớp Block thêm và hàm tính mã hash

public String CalculateHash()
{
  HashSha256 sha256 = new HashSha256();
  String calculatedhash = sha256.Hash(
      previousHash +
      timeStamp.ToString()
      data);
  return calculatedhash;
}

 

.. và thêm hàm này vào contructor của lớp Block

public Block(String data, String previousHash)
{
  this.data = data;
  this.previousHash = previousHash;
  this.timeStamp = DatetimeHandle.GetTime();
  this.hash = CalculateHash(); // thêm hàm này vào cuối để chắc chắn các dữ liệu khác được init trước khi gọi hàm
}

Ok, giờ chúng ta thử kiểm tra lại công việc vừa làm xem…
Trong hàm Main của class program.cs chúng ta sẽ viết đoạn code để hiển thị chuỗi hash

Khối đầu tiên được gọi là genesis block, và vì nó không có khối phía trước nên ta sẽ gán previousHash của nó bằng 0

class Program
{       
  static void Main(string[] args)
  {
    Block genesisBlock = new Block("Hi im the first block", "0");
    Console.WriteLine("Hash for block 1 : " + genesisBlock.hash);

    Block secondBlock = new Block("Yo im the second block", genesisBlock.hash);
    Console.WriteLine("Hash for block 2 : " + secondBlock.hash);

    Block thirdBlock = new Block("Hey im the third block", secondBlock.hash);
    Console.WriteLine("Hash for block 3 : " + thirdBlock.hash);          

    Console.ReadLine();
  }
}

Output của chúng ta sẽ là 3 mã hash

thaotrinh.info__tao-ung-dung-blockchain-dau-tien-voi-c
Mỗi block giờ sẽ chứa chữ kí số được xây dựng trên thông tin của nó và chữ ký số của khối phía trước.
Để giống với khái niệm blockchain hơn, ta sẽ lưu trữ các block này trong List
Chỉnh sửa file program.cs như sau:

class Program
{
  public static List<Block> blockchain = new List<Block>();

  static void Main(string[] args)
  {
    blockchain.add(new Block("Hi im the first block", "0"));		
    blockchain.add(new Block("Yo im the second block",blockchain.ElementAt(blockchain.size()-1).hash)); 
    blockchain.add(new Block("Hey im the third block",blockchain.ElementAt(blockchain.size()-1).hash));

    string printBlockChain = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(blockchain);
    Console.WriteLine(printBlockChain);

    Console.ReadLine();
  }
}

Để sử dụng được JavaScriptSerializer thì các bạn làm như sau

Right click References and do Add Reference, then from Assemblies->Framework select System.Web.Extensions

Chạy lại ứng dụng và xem kết quả thử nhé mọi người 🙂

 

Kiểm tra tính toàn vẹn của blockchain.

Chúng ta sẽ viết phương thức IsChainValid() kiểu boolean trong file program.cs. Phương thức nãy sẽ loop qua tất cả các block trong chain và so sánh các chuỗi hash. Phương thức này sẽ cần kiểm tra biến hash so sánh nó với chuỗi hash được tính toán, và chỗi hash ở khối trước so sánh với biến previousHash. Uhm, nghe có vẻ phức tạp, thử nhìn code xem có dễ hiểu hơn không nào.

public static Boolean IsChainValid()
{
  Block currentBlock;
  Block previousBlock;

  //loop through blockchain to check hashes:
  for (int i = 1; i < blockchain.Count; i++)
  {
    currentBlock = blockchain.ElementAt(i);
    previousBlock = blockchain.ElementAt(i - 1);

    //compare registered hash and calculated hash:
    if (!currentBlock.hash.Equals(currentBlock.CalculateHash()))
    {
      Console.WriteLine("Current Hashes not equal");
      return false;
    }

    //compare previous hash and registered previous hash
    if (!previousBlock.hash.Equals(currentBlock.previousHash))
    {
      Console.WriteLine("Previous Hashes not equal");
      return false;
    }
  }
  return true;
}

Bất kì một thay đổi nào với mỗi block phương thức này sẽ trả về false.

Trên mạng lưới bitcoin, các nodes chia sẻ các blockchain và chuỗi hợp lệ dài nhất sẽ được châp nhận bởi hệ thống. Vậy nếu một hacker giả mạo một khối và tạo nên chuỗi blockchain mới cho mạng thì sao. Điều này là bất khả thi, vì sao? Giả sử hacker có thể xâm nhập và thay đổi một khối dữ liệu, điều này dẫn tới các dữ liệu tiếp theo của khối này cũng bị thay đổi. Vì vậy cần rất nhiều thời gian và sức mạnh tính toán để hacker có thể vượt hơn được so với sức mạnh tính toán của các thành viên còn lại trong hệ thống khi kết hợp.

Đào tiền ảo, cày sâu hay lướt sóng?

Lets start mining blocks!!!

Chúng ta sẽ yêu cầu minner tiến hành đào-coin bằng cách bắt họ phải thử các biến khác nhau trong block cho đến khi chuỗi hash bắt đầu với giá trị 0. Tại sao lại là 0?. Bạn có thể quy ước là bất cứ giá trị gì, tùy thuộc vào genesis hash của bạn. Trong trường hợp này chúng ta chọn 0.

Tiếp theo chúng ta thêm biến nonce trong hàm CalculateHash(), và thêm hàm MineBlock() như sau:

class Block
{
  public String hash;
  public String previousHash;
  private String data; //our data will be a simple message.
  private long timeStamp; //as number of milliseconds since 1/1/1970.
  private int nonce = 0;

  //Block Constructor.
  public Block(String data, String previousHash)
  {
    this.data = data;
    this.previousHash = previousHash;
    this.timeStamp = DatetimeHandle.GetTime();
    this.hash = CalculateHash();
  }

  public String CalculateHash()
  {
    HashSha256 sha256 = new HashSha256();
    String calculatedhash = sha256.Hash(
        previousHash +
        timeStamp.ToString() +
        nonce.ToString() + 
        data);
    return calculatedhash;
  }

  public void MineBlock(int difficulty)
  {
    var str = new String(new char[difficulty]);
    String target = new String(new char[difficulty]).Replace('\0', '0'); //Create a string with difficulty * "0" 
    while (!hash.Substring(0, difficulty).Equals(target))
    {
      nonce++;
      hash = CalculateHash();
    }
    Console.WriteLine("Block Mined!!! : " + hash);
  }
}

Phương thức MineBlock() truyền vào tham số difficulty kiểu int để quy định độ khó của thuật toán. Các bạn có thể thay đổi tham số này để kiểm tra tốc độ đào-coin khi chạy chương trình. Ở ví dụ này mình khuyến khích các bạn đặt ở mức 4-6 cho việc testing. Ex, Hiện tại độ khó của Litecoin là khoảng 442,592, khá kinh khủng.

Chúng ta thêm biến difficulty vào lớp program.cs:

public static int difficulty = 5;

Tiếp đó chúng ta cập nhật lớp program.cs, gọi hàm MineBlock() cho mỗi block mới. Phương thức IsChainValid() cũng được gọi sau khi đào được coin :D.

class Program
{
  public static List<Block> blockchain = new List<Block>();
  public static int difficulty = 5;

  static void Main(string[] args)
  {
    blockchain.Add(new Block("Hi im the first block", "0"));
    Console.WriteLine("Trying to Mine block 1... ");
    blockchain.ElementAt(0).MineBlock(difficulty);

    blockchain.Add(new Block("Yo im the second block", blockchain.ElementAt(blockchain.Count - 1).hash));
    Console.WriteLine("Trying to Mine block 2... ");
    blockchain.ElementAt(blockchain.Count - 1).MineBlock(difficulty);

    blockchain.Add(new Block("Hey im the third block", blockchain.ElementAt(blockchain.Count - 1).hash));
    Console.WriteLine("Trying to Mine block 3... ");
    blockchain.ElementAt(blockchain.Count - 1).MineBlock(difficulty);

    Console.WriteLine("\nBlockchain is Valid: " + IsChainValid());
    string printBlockChain = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(blockchain);
    Console.WriteLine(printBlockChain);

    Console.ReadLine();
  }

  public static Boolean IsChainValid()
  {
    Block currentBlock;
    Block previousBlock;

    //loop through blockchain to check hashes:
    for (int i = 1; i < blockchain.Count; i++)
    {
      currentBlock = blockchain.ElementAt(i);
      previousBlock = blockchain.ElementAt(i - 1);

      //compare registered hash and calculated hash:
      if (!currentBlock.hash.Equals(currentBlock.CalculateHash()))
      {
        Console.WriteLine("Current Hashes not equal");
        return false;
      }

      //compare previous hash and registered previous hash
      if (!previousBlock.hash.Equals(currentBlock.previousHash))
      {
        Console.WriteLine("Previous Hashes not equal");
        return false;
      }
    }
    return true;
  }
}

Chạy thử và xem kết quả nhé

thaotrinh.info__tao-ung-dung-blockchain-dau-tien-voi-c
Ối zồi ôi tôi đào được coin rồi này 😀

Kết

Một blockchain giả mạo sẽ không thể bắt kịp chuỗi dài hơn và hợp lệ trừ khi chúng có tốc độ tính toán lớn hơn tất cả các nút khác trong mạng của bạn kết hợp. Một máy tính lượng tử trong tương lai hay gì đó (người ngoài hành tinh?)

Demo

https://github.com/kumochan/Blockchain-Sample-01

Written by thaotrinh