前言 本文為「Building a Blockchain in Golang 」教學影片的學習筆記。
工作量證明 工作量證明(Proof of Work,簡稱 POW)是一種對應服務與資源濫用,或是阻斷服務攻擊的經濟對策。
在區塊鏈中的工作量證明是指,需要成功完成大量運算處理的人才能成為交易的驗證人,並可以獲得權利將新的區塊打包到區塊鏈的機制。
工作量證明最常用的技術原理是雜湊函式。由於輸入雜湊函式 h
的任意值 n
,會對應到一個 h(n)
結果,而 n
只要變動一個位元,就會引起雪崩效應,所以幾乎無法從 h(n)
反推回 n
,因此藉由指定尋找 h(n)
的特徵,讓使用者進行大量的窮舉運算,就可以達成工作量證明。
實作 在 blockchain
資料夾裡建立一個 proof.go
檔。
1 touch blockchain/proof.go
設置一個「難度」為 12
的常數,用來算出一個經過左移運算後產生的目標值。如果數值越大,設定的目標值就會越小(其雜湊值開頭的 0
就會越多)。
建立一個 ProofOfWork
結構體。
1 2 3 4 type ProofOfWork struct { Block *Block Target *big.Int }
建立一個 NewProof
方法,回傳一個 ProofOfWork
結構體的實體。目標值是一個將 1
經過左移運算後產生的大數,難度越高,則目標值越小。
1 2 3 4 5 func NewProof (b *Block) *ProofOfWork { target := big.NewInt(1 ) target.Lsh(target, uint (255 -Difficulty)) return &ProofOfWork{b, target} }
建立一個 ToHex
方法,幫忙把 int64
型別轉為 []byte
型別。
1 2 3 4 5 6 7 func ToHex (num int64 ) []byte { buff := new (bytes.Buffer) if err := binary.Write(buff, binary.BigEndian, num); err != nil { log.Println(err) } return buff.Bytes() }
為 ProofOfWork
結構體建立一個 InitData
方法,將區塊的 PrevHash
、Data
、nonce
和Difficulty
合併。
1 2 3 4 5 6 7 8 9 func (pow *ProofOfWork) InitData(nonce int ) []byte { return bytes.Join( [][]byte { pow.Block.PrevHash, pow.Block.Data, ToHex(int64 (nonce)), ToHex(int64 (Difficulty)), }, []byte {}) }
為 ProofOfWork
結構體建立一個 Run
方法,建立一套找出 nonce
值的演算法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func (pow *ProofOfWork) Run() (int , []byte ) { var initHash big.Int var hash [32 ]byte nonce := 0 for nonce < math.MaxInt64 { data := pow.InitData(nonce) hash = sha256.Sum256(data) fmt.Printf("\r%x" , hash) initHash.SetBytes(hash[:]) if initHash.Cmp(pow.Target) == -1 { break } nonce++ } fmt.Println() return nonce, hash[:] }
修改 Block
結構體,增加一個 Nonce
屬性。
1 2 3 4 5 6 type Block struct { Hash []byte Data []byte PrevHash []byte Nonce int }
將 block.go
檔的 DeriveHash
方法移除。
1 2 3 4 5 func (b *Block) DeriveHash() { info := bytes.Join([][]byte {b.Data, b.PrevHash}, []byte {}) hash := sha256.Sum256(info) b.Hash = hash[:] }
修改 block.go
檔的 CreateBlock
方法,在建立區塊的時候執行,尋找 nonce
值和雜湊值。
1 2 3 4 5 6 7 8 func CreateBlock (data string , prevHash []byte ) *Block { block := &Block{[]byte {}, []byte (data), prevHash, 0 } pow := NewProof(block) nonce, hash := pow.Run() block.Hash = hash[:] block.Nonce = nonce return block }
為 ProofOfWork
結構體建立一個 Validate
方法,使用區塊的 nonce
來進行驗證,即 initHash
如果剛好小於目標值,則代表驗證成功。
1 2 3 4 5 6 7 func (pow *ProofOfWork) Validate() bool { var initHash big.Int data := pow.InitData(pow.Block.Nonce) hash := sha256.Sum256(data) initHash.SetBytes(hash[:]) return initHash.Cmp(pow.Target) == -1 }
修改 main.go
檔,將工作量證明結果印出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func main () { chain := blockchain.InitBlockChain() chain.AddBlock("First Block after Genesis" ) chain.AddBlock("Second Block after Genesis" ) chain.AddBlock("Third Block after Genesis" ) for _, block := range chain.Blocks { fmt.Printf("Previous Hash: %x\n" , block.PrevHash) fmt.Printf("Data in Block: %s\n" , block.Data) fmt.Printf("Hash: %x\n" , block.Hash) pow := blockchain.NewProof(block) fmt.Printf("Pow: %s\n" , strconv.FormatBool(pow.Validate())) fmt.Println() } }
執行程式。
顯示結果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 00031a02a972efd4fa6ea999407149b85b03ccecb8c2bb8eb5a1d068862309d0 0004458722d47515269d8ddbe22e2a2b5a260bd9359a3b7d72a9888b14f9f5f5 000589525b1a774b7d1ffcbf471d32eccea3a8f826c463dffdf09a2261c0be12 000666528af011921e9f471b07eb46a4d08edce58435df868e9dc85726ff0eda Previous Hash: Data in Block: Genesis Hash: 00031a02a972efd4fa6ea999407149b85b03ccecb8c2bb8eb5a1d068862309d0 Pow: true Previous Hash: 00031a02a972efd4fa6ea999407149b85b03ccecb8c2bb8eb5a1d068862309d0 Data in Block: First Block after Genesis Hash: 0004458722d47515269d8ddbe22e2a2b5a260bd9359a3b7d72a9888b14f9f5f5 Pow: true Previous Hash: 0004458722d47515269d8ddbe22e2a2b5a260bd9359a3b7d72a9888b14f9f5f5 Data in Block: Second Block after Genesis Hash: 000589525b1a774b7d1ffcbf471d32eccea3a8f826c463dffdf09a2261c0be12 Pow: true Previous Hash: 000589525b1a774b7d1ffcbf471d32eccea3a8f826c463dffdf09a2261c0be12 Data in Block: Third Block after Genesis Hash: 000666528af011921e9f471b07eb46a4d08edce58435df868e9dc85726ff0eda Pow: true
程式碼
參考資料