區塊鏈是二十一世紀最具革命性的技術之一,它正在不斷成熟,它的諸多潛力正在逐步實現中。本質上來看,區塊鏈只不過是一個分布式的數據庫。之所以區塊鏈獨特,是因為它并不是一個私有數據庫,而是一個公開的數據庫,即,每一個使用它的人擁有這個數據庫的全部或者至少一部分。任何一個新的數據記錄,只能在多數數據庫持有者(維護者)的多數同意之后被加入數據庫。正因如此,區塊鏈使得加密貨幣以及智能合約成為可能。
?
在這個系列文章中,我們將打造一個簡化版本的加密貨幣,它將基于一個簡化版本的區塊鏈實現。
區塊(Block)
讓我們先從區塊開始。在區塊鏈里,價值信息存儲在區塊之中。比如,比特幣的區塊存儲交易記錄,而交易記錄是任何加密貨幣的核心。除此之外,區塊里還包含有技術信息,比如它的版本號,當前的時間戳,以及上一個區塊的哈希(Hash)。
在這篇文章中,我們所實現的并不是像比特幣那樣完整的區塊鏈,而是一個簡化版本的區塊鏈,它只含有最基本的核心信息。差不多是這樣:
type Block struct {
Timestamp int64
Data []byte
PrevBlockHash []byte
Hash []byte}
Timestamp 是當前的時間戳(即,區塊被創建的時間),Data 是區塊中包含的價值信息,PrevBlockHash 存儲的是上一個區塊的哈希,而Hash 保存的是當前區塊的哈希。在比特幣的標配中,Timestamp、PrevBlockHash、Hash是區塊的頭部數據(Block headers),構成一個單獨的數據結構;而交易記錄(Transactions,在我們這個版本中就是 Data),是另外一個單獨的數據結構。而我們在這里為了簡化,把數據結構混在了一起。
那我們如何計算哈希呢?計算哈希的方式是區塊鏈的重要特征之一,也正是這個特性使得區塊鏈如此安全。關鍵在于,計算哈希是一個計算起來很困難的工作,它需要時間,哪怕是在很快的計算機上(這就是為什么人們要買比 CPU 計算能力更強悍 GPU 甚至專門的 ASIC 芯片做礦機的 原因)。這是故意如此設計的,這么做的結果是,往區塊鏈(數據庫)里添加新的區塊(數據)有一定的困難,以此保證一旦新的數據被加入,往后很難篡改。以后的文章里會進一步討論并實現這個機制。
現在呢,我們只需要罷區塊里的各個字段關聯起來,并在此基礎上計算出一個 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[:]
}
接下來,依據 Golang 的常用方式,我們將實現一個函數,以便更簡單地創建區塊:
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}
block.SetHash() return block
}
就這么簡單。
區塊鏈(Blockchain)
現在,讓我們來實現區塊鏈。本質上來看,區塊鏈只不過是一個特定結構的數據庫,它是一個有序的,反向鏈接的列表(back-linked list)。這就意味著說,區塊是按照插入的順序排列的,每個區塊都鏈接到上一個區塊。這樣的結構,使得使用者可以很快地在區塊鏈中獲得最新的區塊,也可以很有效率地通過區塊的哈希獲得某個區塊。
在 Golang 中,這種結構可以用數組(Array)與數圖(Map) 實現:數組用來維護有序哈希(在 Go 語言中,數組是有序的);數圖(Map) 用來維護 hash → block 對。不過,在我們的區塊鏈原型中,我們只需要數組就可以了,因為我們暫時不需要通過哈希獲取區塊。
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)
}
這就完事兒了?或者……?
為了添加新的區塊,我們需要一個已經存在的區塊,可現在我們的區塊鏈里面沒有任何區塊!于是,在任何區塊鏈中,應該至少有一個區塊,這第一個區塊,被稱為“創始塊”(Genesis Block)。來,讓我們實現一個方法去創建這個“創始塊”:
func NewGenesisBlock() *Block { return NewBlock(“Genesis Block”, []byte{})
}
現在我們就可以創建一個函數,用來創建一個已含有“創始塊”的區塊鏈了:
func NewBlockchain() *Blockchain { return &Blockchain{[]*Block{NewGenesisBlock()}}
}
讓我們來看看這區塊鏈是否能用?
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: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168
Prev. hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168
Data: Send 1 BTC to Ivan
Hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1
Prev. hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1
Data: Send 2 more BTC to Ivan
Hash: 561237522bb7fcfbccbc6fe0e98bbbde7427ffe01c6fb223f7562288ca2295d1
(竟然)完工!
結論
我們創建了一個極簡的區塊鏈原型:它只不過是一個由區塊構成的數組,每個區快鏈接指向上一個區塊。當然,真正的區塊鏈遠比這個復雜的多。在我們的區塊鏈里,添加一個新區塊非常快,非常容易;但是在真正的區塊鏈中添加一個新的區塊需要更多的工作:在獲得添加區塊的允許之前要做很繁重的計算才行(這個過程被稱為工“作證明機制”,即,“Proof-of-Work”,POW)。并且,區塊鏈是一個沒有主權的分布式的數據庫。因此,任何一個新的區塊在被加入之前,必須經過網絡中其它參與者的確認與允許(這個機制被稱為“共識機制”,“Consensus”)…… 還有,我們的區塊鏈里,還沒有任何交易記錄呢!
評論
查看更多