Trong các bài viết trước trên blog của Vietnam Lab Center, đã có khá nhiều bài viết giới thiệu về blockchain và tiền ảo. Lần này với mục đích hiểu thêm về blockchain và đồng thời học thêm một ngôn ngữ mới là Go, người viết quyết định dịch (có biến tấu) một series bài viết về chủ đề lập trình Blockchain bằng ngôn ngữ Go và giới thiệu trên blog Vietnam Lab. Phần đầu tiêu của loạt bài viết sẽ giới thiệu qua về ngôn ngữ Go, đồng thời khởi tạo code ban đầu cho dự án. Link cho các phần sau cũng sẽ được update ở đầu mỗi bài viết

Các khái niệm cơ bản về blockchain

Để có thể hiểu nội dung bài viết một cách tốt nhất, người đọc nên có những hiểu biết cơ bản về blockchain. Điều đấy có thể dễ dàng thu được qua các bài viết trên chính blog của Vietnam Lab:

Giới thiệu về ngôn ngữ Go

Go là một ngôn ngữ lập trình được ra đời tại Google vào năm 2009 với các tác giả là những cây đa, cây đề trong giới lập trình như Rob Pike hay Ken Thompson.

Go ra đời với mục đích trước nhất là đáp ứng yêu cầu của Google, một ngôn ngữ có tốc độ cao, dễ sử dụng, không quá phức tạp, thích hợp cho việc lập trình hệ thống ở mức không quá low-level (Java, C++ quá phức tạp, Python dễ dùng nhưng lại chậm, C tuy syntax đơn giản, nhưng khó dùng và quá low-level).

Với tác giả là những người làm ở Bell Labs và có đóng góp rất lớn cho hệ điều hành Unix, nên Go chịu ảnh hưởng rất lớn từ C về syntax với những cải tiến để khiến code dễ đọc hơn. Tuy nhiên khác với C, Go sử dụng garbage collection thay vì việc quản lý memory thủ công và do vậy dễ sử dụng hơn nhưng tốc độ cũng chậm hơn. Go cũng là một ngôn ngữ static giống như Java, C++ nhưng đơn giản hơn nhiều (không hỗ trợ class, inheritance hay generic). Go cũng hỗ trợ rất tốt cho việc lập trình đa luồng hay networking.

Bạn đọc có thể tìm hiểu về ngôn ngữ Go tại trang chủ của ngôn ngữ này: https://golang.org/. Đồng thời sử dụng thử ngay trên web tại trang: https://tour.golang.org

Blockchain bằng Go - 1st step

1. Khởi tạo dự án Go

Sau khi đã cài đặt Go và thiết lập GOPATH như hướng dẫn cài đặt trên trang chủ của Go, ta cài đặt công cụ dep, công cụ quản lý dependencies của Go.

$ curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh

Sau đó ta sử dụng dep để tạo mới một dự án Go:

$ mkdir $GOPATH/src/gochain
$ cd $GOPATH/src/gochain
$ dep init

Trong folder gochain, ta sẽ có các file and folders như sau:

gochain
|- vendor/
|- Gopkg.lock
|- Gopkg.toml

Những file và folder trên sẽ phục vụ cho việc quản lý dependencies về sau. Ta sẽ không dùng đến chúng trong bài viết này

2. Những dòng code đầu tiên cho Block

Rõ ràng với blockchain, block đóng vai trò quan trọng nhất và cũng là thứ đầu tiên ta cần code. Trước hết, ta tạo file block.go và taọ cấu trúc dữ liệu cho block như sau

// block.go
package main

import (
	"bytes"
	"time"
	"strconv"
	"crypto/sha256"
)

// A Block in the chain
type Block struct {
	Timestamp     int64
	Data          []byte
	PrevBlockHash []byte
	Hash          []byte
}

Các bạn nào đã quen thuộc với C, thì struct trong Go cũng có ý nghĩa tương tự như struct trong C vậy. Nó là một cấu trúc dữ liệu với các trường được định nghĩa sẵn. Ở đây, block là một struct với các trường timestamp, data của block này, hash của block trước và hash của block này. Tất nhiên, block trong blockchain của bitcoin hay các đồng tiền ảo khác sẽ phức tạp hơn và chứa nhiều thông tin hơn rất nhiều.

Tiếp theo, ta sẽ add thêm hàm để tính hash cho data của block hiện tại:

// block.go
func (b *Block) SetHash() {
	timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))
	headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{})
	hash := sha256.Sum256(headers)

	b.Hash = hash[:]
}
  • Đây là một method với tên SetHash được gọi trên một pointer đến một struct Block. Pointer trong Go cũng giống như trong C là một con trỏ chỉ đến địa chỉ memory chứa giá trị của biến số. Vì hàm trong Go cũng như C mang tính chất copy by value nên khi truyền tham số vào trong hàm, biến số được sử dụng sẽ là bản copy có cùng giá trị của tham số chứ không phải là bản thân tham số đấy. Dù ở đây SetHash là một method được gọi trên struct Block nhưng nguyên tắc này vẫn không thay đổi. Nếu muốn thay đổi gía trị của struct này, ta cần gọi method trên một pointer đến struct đó.
  • Nội dung code của method này thì cũng khá dễ hiểu, ai nếu đọc qua về cách tạo hash 1 block của blockchain đều sẽ có thể hiểu được: Lấy timestamp hiện tại, join lần lượt với hash của block trước, data của block này, sau đó dùng hàm sha256 để tính hash.

Đã có method để tính hash cho block, tiếp theo sẽ là function để tạo một block mới:

// block.go
func NewBlock(data string, prevBlockHash []byte) *Block {
	block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}
	block.SetHash()
	return block
}
  • Function SetHash ở trên được gọi là method vì nó được gọi trên một đối tượng khác (struct), còn NewBlock ở đây là một hàm bình thường trong Go với 2 tham số dataprevBlockHash và giá trị trả về là một pointer đến Block.
  • Nội dung hàm cũng khá đơn giản: Khởi tạo một block mới, lấy address trên memory của nó gán cho một biến pointer rồi dùng hàm SetHash trên biến này để tính hash của block mới.

3. Blockchain sơ khai

Blockchain nếu hiểu đơn giản là chain của nhưng block, và ta cũng sẽ lập trình ban đầu nó đơn giản như vậy. Tạo file blockchain.go với nội dung như sau:

// blockchain.go
package main

type Blockchain struct {
	blocks []*Block
}
  • Blockchain ở đây chỉ đơn giản là một struct với một trường blocks là một slice các pointer Block. slice trong Go tương tự như array, nhưng không cần phải cố định độ lớn khi khai báo (slicearray có mối liên hệ mật thiết, bạn đọc có thể tìm hiểu thêm)

Tiếp đến là hàm khởi tạo blockchain và hàm tạo block Genesis (block đầu tiên trong blockchain):

// blockchain.go
func NewGenesisBlock() *Block {
	return NewBlock("Genesis Block", []byte{})
}

func NewBlockchain() *Blockchain {
	return &Blockchain{[]*Block{NewGenesisBlock()}}
}
  • Hàm NewGenesisBlock để tạo block đầu tiên khá đơn giản, chỉ là gọi đến hàm NewBlock trong file block.go với tham số: data của block đầu tiên và hash của block trước là rỗng. Vì 2 file code đều sử dụng chung package main nên có thể gọi thoải mái hàm và biến số public giữa các file

  • Hàm NewBlockchain để tạo 1 blockchain mới thì chỉ là tạo một struct Blockchain với trường blocks chỉ có Genesis block. Sau đó thì trả về địa chỉ trên memory của struct này.

Cuối cùng chỉ cần thêm hàm với tính năng add một block mới vào blockchain là ta đã có một blockchain tối giản:

// blockchain.go
// AddBlock add new block
func (bc *Blockchain) AddBlock(data string) {
	prevBlock := bc.blocks[len(bc.blocks)-1]
	newBlock := NewBlock(data, prevBlock.Hash)
	bc.blocks = append(bc.blocks, newBlock)
}
  • Đây cũng là một method gọi trên pointer của một struct Blockchain. Nội dung hàm thì sẽ lấy hash của block cuối cùng trong blockchain hiện tại; dùng hàm NewBlock để khởi tạo block mới với data được truyền vào từ tham số. Sau đó thêm block mới vào cuối chuỗi các block hiện tại.

4.Test thử một blockchain tối giản.

Vậy là ta bước đầu đã code được prototype của một blockchain tối giản. Và đây là đoạn code để ta test thử thành quả:

// main.go
package main

import (
	"fmt"
)

func main() {
	bc := NewBlockchain()

	bc.AddBlock("Linh Dep Trai")
	bc.AddBlock("Linh Dep Trai x2")

	for _, block := range bc.blocks {
		fmt.Printf("Prev block's hash: %x\n", block.PrevBlockHash)
		fmt.Printf("Current block's hash: %x\n", block.Hash)
		fmt.Printf("Current block's data: %x\n", block.Data)
		fmt.Println()
	}
}

Trong đoạn code trên, ta tạo một blockchain, thêm 1 vài block mới sau đó loop qua từng block trong chain và in các thống tin của block đó.

Build và chạy thử code test:

$ cd $GOPATH/src/gochain
$ go build
$ ./gochain
Prev block's hash:
Current block's hash: b8f50cc397ff08c3417df8408db3ee9073c7a17652fd4c0ba4868b3c3eb7b228
Current block's data: Genesis Block

Prev block's hash: b8f50cc397ff08c3417df8408db3ee9073c7a17652fd4c0ba4868b3c3eb7b228
Current block's hash: 6f3e44b7a8f203513828e2a49119186a0cd66f922290a65f56c0def228a6b28b
Current block's data: Linh Dep Trai

Prev block's hash: 6f3e44b7a8f203513828e2a49119186a0cd66f922290a65f56c0def228a6b28b
Current block's hash: 66719538f9e1ed99d44ac4a3f31a0fff95452a4519de4f9ab615155f3c515193
Current block's data: Linh Dep Trai x2

Kết bài

Vậy là sử dụng những tính năng cơ bản của ngôn ngữ Go, ta đã có thể code được một blockchain với tính năng tối giản nhất có thể. Phần tiếp theo ta sẽ thêm một tính năng quan trọng tương tự như blockchain của Bitcoin: Proof of Work

Tham khảo