用go編寫區塊鏈系列之1---基本協議
本文將要用go語言實現一個最簡單的區塊鏈,更多內容請參考https://jeiwan.cc/posts/building-blockchain-in-go-part-1/。
1 區塊
區塊是區塊鏈組成單元,區塊鏈就是由一個一個區塊串聯而成。區塊鏈是一個不斷向後延伸的區塊的連結串列。這裡定義一個最簡單的區塊結構:
type Block struct {
Timestamp int64
Data []byte
PrevBlockHash []byte
Hash []byte
}
Timestamp是時間戳,儲存建立這個區塊的時間戳。Data是這個區塊要儲存的資料。Hash是對這個區塊資料進行Hash運算得到的Hash值。PrevBlockHash是前面一個區塊的Hash值。
如何計算區塊Hash?計算區塊Hash是區塊鏈的一個重要特性,它也是保證區塊鏈安全的手段。計算Hash值是一種很耗費計算資源的操作,它增加了向區塊鏈增加區塊的難度,這是增進區塊鏈安全的一種手段。
我們使用SHA-256演算法來計算Hash值,SHA-256可以保證對任意長度的資料,計算得到的Hash值都是256位。它滿足不可逆性,即很難從Hash值去推導元資料。只要對元資料進行一點小改動,Hash值將會變化很大。我們的計算區塊Hash的函式為:
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[:] }
這個函式把block的Timestamp、Data和PrevBlockHash拼接起來再計算hash值。
建立一個新區快的方法是:
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}
block.SetHash()
return block
}
2 區塊鏈
區塊鏈是區塊的連結串列,在這裡我們使用陣列來表示區塊鏈。我們定義的型別為:
type Blockchain struct { blocks []*Block }
給Blockchain增加新增新區快的方法:
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{})
}
可以定義生成Blockchain的方法了:
func NewBlockchain() *Blockchain {
return &Blockchain{[]*Block{NewGenesisBlock()}}
}
3 讓區塊鏈工作起來
所有程式碼:
package main
import (
"strconv"
"bytes"
"crypto/sha256"
"time"
"fmt"
)
type Block struct {
Timestamp int64
Data []byte
PrevBlockHash []byte
Hash []byte
}
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
}
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 Block
Hash: b81cf38880f767af3c18d4a5417400a2fd2378c47d4ecb32e525cf1eedfb4a4f
Prev. hash: b81cf38880f767af3c18d4a5417400a2fd2378c47d4ecb32e525cf1eedfb4a4f
Data: Send 1 BTC to Ivan
Hash: 4cec5aa5b6503ac96d8b9a5e27159d32b954d262499fca88c4cc6f0beb454458
Prev. hash: 4cec5aa5b6503ac96d8b9a5e27159d32b954d262499fca88c4cc6f0beb454458
Data: Send 2 more BTC to Ivan
Hash: 866b1368c058b76dc0c88826aa3531b42803d481d22bfb6cca1361885a179674
4 結論
在這篇文章裡我門建立了一個最簡單的區塊鏈,我門的區塊鏈只是一個區塊的陣列,每個區塊儲存上一個區塊的Hash。實際的區塊鏈遠比這個複雜。在我門這個區塊鏈中,新增新區快非常容易,但是在實際的區塊鏈中新增新區塊需要做大量有難度的計算工作以獲得新增新區快的許可權。此外,由於區塊鏈不會只有一個節點,一個新區塊被礦工生產出來,還需要經過其它節點的驗證才能寫入資料庫。此外,我們的區塊鏈中還沒有新增交易。
這些都將在後續文章實現。