本篇開始進入"用Go構建區塊鏈"系列,主要對原文進行翻譯。本篇對應原文如下:
Building Blockchain in Go. Part 1: Basic Prototype
話不多說,開始進入本文。
1、介紹
區塊鏈是21世紀最具革命性的技術之一,它仍在逐步發展中,並且其潛力還未被充分認識。本質上,區塊鏈只是一個分散式資料庫而已。但是,它的獨特之處在於它不是一個私人的資料庫,而是一個公用的,即每個使用它的人都擁有它的全部或部分複本。只有得到其他資料庫管理員的同意,新的記錄才能被加入。正因為由此區塊鏈,才使得加密貨幣和智能合約成為可能。
在本系列文章中,我們將構建一個基於簡單區塊鏈實現的簡單加密貨幣。
2、區塊
我們從"區塊鏈"的"區塊"部分開始。在區塊鏈中,它儲存有價值的資訊。例如,比特幣Block Storage交易資訊,這是所有加密貨幣的本質。除此之外,區塊還包含一些技術資訊,如版本號碼、目前時間戳和上一個區塊的雜湊。
在本文中,我們不會實現區塊鏈中描述的區塊,也不會是比特幣技術規範中的區塊,而是使用簡化版本,其中只包含重要訊息。這是它的樣子:
type Block struct { Timestamp int64 Data []byte PrevBlockHash []byte Hash []byte}
Timestamp
是目前時間戳(區塊被建立時), Data
是包含在區塊中的實際有價值的資訊,而 Hash
是當前區塊的雜湊。在比特幣技術規範中, Timestamp
,PrevBlockHash
和 Hash
是區塊頭,它們形成一個單獨的資料結構,而交易(在我們的例子中是資料)是一個獨立的資料結構。我們這裡簡單起見,混合在一起了。
那麼,怎麼計算雜湊呢?雜湊的計算方式在區塊鏈中是一個非常重要的特性,正是這一特性使得區塊鏈更加安全。問題是計算雜湊是一個難以計算的操作,即使在很快的電腦上也需要話費很多時間(這就是為什麼人們購買強大的GPU來挖比特幣)。這是一個架構上有意為之的設計,這使得添加新的區塊變得困難,從而阻止添加後的修改。我們將在接下來的文章中去討論和實現這個機制。
現在,我們只取了區塊欄位,並把它們拼接起來,並在串連的組合上計算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}
好了,這就是區塊!
3、區塊鏈
現在我們來實現一個區塊鏈。其本質區塊鏈僅僅是一個具有特定結構的資料庫:它是一個有序的,尾部相連的鏈表。這意味著區塊按照插入的順序來儲存,並且每個區塊都連結著前一個區塊。該結構允許快速擷取鏈中的最新塊,並通過其雜湊(有效)擷取區塊。
在Golang中,這個結構可以通過使用array和map來實現:array儲存有序的雜湊 (Golang中,array是有序的),並且 map 結構可以儲存 hash -> block
的匹配資訊。但在我們的區塊鏈原型中,我們只會使用一個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)}
就這樣!還是?
要添加新的區塊,我們需要一個已存在的區塊,然而我們的區塊鏈上還沒有一個區塊!因此,在任何區塊鏈中,必須至少有一個區塊,而這個區塊是鏈中的第一個區塊,稱為創世區塊。讓我們實現一個建立創世區塊的函數:
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 BlockHash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168Prev. hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168Data: Send 1 BTC to IvanHash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1Prev. hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1Data: Send 2 more BTC to IvanHash: 561237522bb7fcfbccbc6fe0e98bbbde7427ffe01c6fb223f7562288ca2295d1
沒錯,就這樣!
4、總結
我們構建了一個非常簡單的區塊鏈原型:它只是一個包含有區塊的數組,每個區塊和前一個相串連。真實的區塊鏈比這個複雜得多。在我們的區塊鏈中,添加新的區塊非常簡單而且很好,但是在真實的區塊鏈中添加新區塊需要做很多工作:一是需要在添加區塊前做一些複雜的計算來擷取添加區塊的許可權(這個過程被稱為 工作量證明
)。另外,區塊鏈是一個分散式資料庫,其沒有單一的決策者。因此,一個新的區塊必須得到網路的其他參與者的確認和同意(這種機制被稱為共識)。而且,我們的區塊鏈還沒有交易!
在以後的文章中,我們將介紹這些功能。
連結:
1.擷取源碼:https://github.com/Jeiwan/blockchain_go/tree/part_1
2.區塊雜湊演算法:https://en.bitcoin.it/wiki/Block_hashing_algorithm
由於水平有限,翻譯品質不太好,歡迎大家拍磚。