2.1 基本概念

尹成發表於2018-11-08

區塊鏈是21世紀重要的技術革命之一,雖然還沒有成熟,但是仍然有很多潛力尚待發掘。基於區塊鏈的本質,它就是一個分散式記錄資料庫。但是和私有資料庫不同的是,區塊鏈是公開的(私有鏈在域內也是公開的),也就是說每一個使用它的人都會有完整或者說部分副本。而且新的記錄要增加的話,需要得到鏈中其它擁有者的同意。而且,區塊鏈使得加密貨幣和智慧合約成為可能。這一系列文章,將會闡述和實現基於簡單的區塊鏈來生成簡單的加密貨幣。

Block 塊/區塊

先從“區塊鏈”中的“區塊”說起。在區塊鏈中,塊儲存了變數資訊,比如,比特幣的區塊儲存了交易、還有加密貨幣除了這些,區塊包含了一些技術資訊,比如版本、時間戳、還有排在前面的一個區塊的hash值 
1. Timestamp 時間戳也即是在區塊被建立時的時間 
2. Data 就是這個區塊儲存的變數資訊 
3. PrevBlockHash 前一區塊的hash值 
4. Hash 是當前區塊的hash值 
和比特幣分開儲存的資料結構不同的是 Timestamp、PrevBlockHash、Hash是區塊的頭(headers)資訊,交易(transactions,我們這裡轉成Data來稱呼)是在資料(data)資訊中。這裡把這些概念放在一塊,方便些:

塊程式碼結構

type Block struct {
  Timestamp     int64
  Data          []byte
  PrevBlockHash []byte
  Hash          []byte
}

hash

那為什麼要計算hash呢?計算hash值在區塊鏈中是非常重要的特點,這使得區塊鏈是安全的。因為計算有指定特徵的hash非常困難,即使在牛逼的計算機中也要花上一些時間計算出來(所以有的人就買更適合簡單浮點運算的GPU去挖Bitcoin礦)。這麼做是故意的,因為這樣可以增加建立新塊的難度,導致增加了區塊的節點無法在增加後改動這個區塊,而改動後,這個區塊也就失效了,不被大家承認。 區塊鏈的hash演算法。為了簡單,我們現在基於SHA-256構造SetHash方法,

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[:]
}

建立區塊

實現一個簡單的建立區塊方法:

func NewBlock(data string, prevBlockHash []byte) *Block {
  block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}
  block.SetHash()
  return block
}

blackchain 區塊鏈

開篇說過,區塊鏈的本質就是一個一定結構的資料庫。它是一個有序的、首尾相連的鏈狀列表,區塊們都是順序、每一個塊都連線著前面的一個塊。這個結構使得可以在區塊鏈中快速找到最後一個區塊,尤其是可以通過hash值找到區塊。

定義簡單的區塊鏈

在golang裡可以使用陣列、map來實現,陣列可以保證順序,map實現hash->block組合的對映不過,針對目前的進度,我們不需要實現能過hash找到區塊的方法,所以這裡只用陣列來保證順序即可。

type Blockchain struct {
  blocks []*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)
}

創世區塊

為了建立新的區塊,需要一個已經存在的區塊,但是現在還沒有任何一個區塊。而在區塊鏈中,第一個區塊,就是“創世區塊”。

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

使用創世區塊來引導區塊鏈

func NewBlockchain() *Blockchain {
  return &Blockchain{[]*Block{NewGenesisBlock()}}
}

執行

現在可以在命令列時輸入 go run *.go執行建立區塊鏈

func main() {
  bc := NewBlockchain()
bc.AddBlock("Send 1 BTC to Ivan")
  bc.AddBlock("Send 2 more BTC to Ivan")
for _, block := range bc.blocks {
    fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash)
    fmt.Printf("Data: %s\n", block.Data)
    fmt.Printf("Hash: %x\n", block.Hash)
    fmt.Println()
  }
}

控制檯會輸出區塊鏈的內部資訊:

Prev. hash:
Data: Genesis Block
Hash: 4f729464de88e6a01c59a54707b11d3efd5fb036637fa81f4fbcce437b7b0738
Prev. hash: 4f729464de88e6a01c59a54707b11d3efd5fb036637fa81f4fbcce437b7b0738
Data: Send 1 BTC to Ivan
Hash: 53655b661290d9d4c9973618dfd2b5cb71c8c2981b5c955fa70af5d6a30b02be
Prev. hash: 53655b661290d9d4c9973618dfd2b5cb71c8c2981b5c955fa70af5d6a30b02be
Data: Send 2 more BTC to Ivan
Hash: 22cbee453308893beca8fd023c77d61a05eca29db99272a2795d0ae7af7d306d

本章總結

我們建立了簡單的區塊鏈原型:只有一個陣列來維護的鏈,每個塊都擁有前一個塊的hash值來保證彼此的連線。真正的區塊鏈自然是要比這裡的複雜得多的。這裡的區塊鏈產生很簡單也很快,但是真正的區塊鏈產生需要做很多工作,如果要獲得一個區塊,那麼需要做大量而繁重的計算,這一機制被稱為工作量證明(Proof-of-Work)。區塊鏈是分散式的且沒有決定者(去中心化)。這就是說,新的區塊增加需要得到其它網路中參與運算的節點認可(共識)。在我們上面的例子中,還沒有一筆交易,所以,不算正式意義上的區塊鏈。