Introduction
區塊鏈是21世紀最具革命性的技術之一,至今仍在發展,很多潛力尚未完全實現。從本質上講,區塊鏈只是一個分散式資料庫。但是它的獨特之處在於它不是私人資料庫,而是公共資料庫,也就是說,使用它的每個人都有完整或部分資料副本。只有在資料庫的其他管理員同意的情況下才能新增新記錄。此外,正是由於區塊鏈才使加密貨幣和智慧合約成為可能。
在本系列文章中,我們將構建一個基於簡單區塊鏈實現的簡化加密貨幣。
Block
讓我們從“區塊鏈”的“區塊”部分開始。在區塊鏈中,區塊是用來儲存有價值資訊的。實際上,交易資訊是所有加密貨幣的價值所在。除此之外,區塊包含一些技術資訊,如版本,當前時間戳和前一個塊的雜湊值。在本文中,我們不打算實現像比特幣規範中描述的那種區塊,而是將使用它的簡化版本,它只包含重要資訊。這是它的樣子:
type Block struct {
Timestamp int64
Data []byte
PrevBlockHash []byte
Hash []byte
}
複製程式碼
Timestamp欄位是當前時間戳(建立區塊時),Data欄位是區塊中包含的實際有價值的資訊,PrevBlockHash欄位儲存前一個區塊的雜湊值,Hash欄位是當前區塊的雜湊值。在比特幣規範中Timestamp, PrevBlockHash, 和 Hash是塊頭,它形成一個單獨的資料結構,Data是另一個資料結構。我們為了簡單起見將它們混合在了一起。
那麼我們如何計算雜湊?雜湊計算方法是區塊鏈的一個非常重要的特徵,正是這個特徵使區塊鏈變得安全。問題在於計算雜湊是一個計算上很困難的操作,即使在快速計算機上也需要一些時間(這就是人們購買功能強大的GPU來挖掘比特幣的原因)。這是一種有意的架構設計,這使得新增新的區塊變得困難,從而防止它們在新增後進行修改。我們將在以後的文章中討論並實現這種機制。
目前,我們僅取了 Block
結構的部分欄位(Timestamp
, Data
和 PrevBlockHash
),並將它們相互拼接起來,然後在拼接後的結果上計算一個 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
現在讓我們實現一個區塊鏈。從本質上講,區塊鏈只是一個具有特定結構的資料庫:它是一個有序的反向連結列表。這意味著區塊以插入順序儲存,並且每個區塊都連結到前一個區塊。這種結構允許快速獲取鏈中的最新區塊並通過其雜湊(有效地)獲得區塊。
在 Golang 中,可以通過一個 array 和 map 來實現這個結構:array 儲存有序的雜湊(Golang 中 array 是有序的),map 儲存 hash -> block 對(Golang 中, map 是無序的)。 但是在基本的原型階段,我們只用到了 array,因為現在還不需要通過雜湊來獲取區塊。
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()
}
}
複製程式碼
Output:
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
複製程式碼
Conclusion
我們構建了一個非常簡單的區塊鏈原型:它只是一個區塊陣列,每個區塊都與前一個區塊連線。實際的區塊鏈顯然複雜得多。在我們的區塊鏈中新增新區塊很容易而且很快,但在實際區塊鏈中新增新區塊需要一些工作:在獲得新增區塊的許可權之前必須執行一些繁重的計算(這種機制稱為工作量證明)。此外,區塊鏈是一個沒有單一決策者的分散式資料庫。因此,必須由網路的其他參與者確認和批准新的區塊(該機制稱為共識)。我們的區塊鏈中還沒有交易!
在以後的文章中,我們將介紹這些功能。
更多文章歡迎訪問 http://www.apexyun.com/
聯絡郵箱:public@space-explore.com
(未經同意,請勿轉載)