前言
在前一篇“golang-區塊鏈學習01”的基礎上,增加我們區塊鏈的工作量證明。
知識點
1、區塊鏈ProofOfWork(工作量證明)概念,因為所有人都想產生區塊來擷取獎勵,為了公平起見,我們規定要想成功產生一個區塊必須完成指定難度的任務才行。也就是誰先完成指定難度的任務就將成功產生一個區塊。先預留個彩蛋,結合執行個體的工作量證明將在文末總結。
golang實現簡單的工作量證明
1、定義一個工作量難度。比如要求生產的區塊的hash值前面五位必須為0。即hash類似:00000xxxxxxxxxxx的樣式。
2、在Block的結構中增加一個Nonce變數,通過不斷修改Nonce的值,不斷計算整個區的hash值,直到滿足上面的要求即可。
3、代碼執行個體
建立一個proofofwork.go檔案。定義一個工作量證明的結構體
type ProofOfWork struct { block *Block // 即將產生的區塊對象 target *big.Int //產生區塊的難度}
建立執行個體化工作量證明結構體.
const targetBits = 20func NewProofOfWork(b *Block) *ProofOfWork { target := big.NewInt(1) //難度:target=10的18次方(即要求計算出的hash值小於這個target) target.Lsh(target, uint(256-targetBits)) pow := &ProofOfWork{b, target} return pow}
計算hash值的演算法
func (pow *ProofOfWork) Run() (int, []byte) { var hashInt big.Int var hash [32]byte nonce := 0// 從0自增 fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data) // 迴圈從nonce=0一直計算到nonce=2的64次方的值,知道算出符合要求的hash值 for nonce < maxNonce { // 準備計算hash的資料 data := pow.prepareData(nonce) hash = sha256.Sum256(data)// 計算hash fmt.Printf("\r%x", hash) hashInt.SetBytes(hash[:]) // 難度證明 if hashInt.Cmp(pow.target) == -1 { break// 符合 } else { nonce++// 不符合繼續計算 } } fmt.Printf("\n\n") return nonce, hash[:]}
準備資料
func (pow *ProofOfWork) prepareData(nonce int) []byte { data := bytes.Join([][]byte{ pow.block.PrevBlockHash, pow.block.Data, IntToHex(pow.block.TimeStamp), IntToHex(int64(targetBits)), IntToHex(int64(nonce)), }, []byte{}) return data}
附件
慣例上碼。所有的代碼檔案清單。
/lession02/src/coin/main.go
package mainimport ( "fmt" "core" "strconv")func main() {fmt.Printf("%d\n",uint(256-20)) bc := core.NewBlockChain() bc.AddBlock("send 1 btc to Ivan") bc.AddBlock("send 2 btc to Ivan") for _, block := range bc.Blocks { fmt.Printf("PrevBlockHash:%x\n", block.PrevBlockHash) fmt.Printf("Data:%s\n", block.Data) fmt.Printf("Hash:%x\n", block.Hash) fmt.Printf("TimeStamp:%d\n", block.TimeStamp) fmt.Printf("Nonce:%d\n", block.Nonce) pow := core.NewProofOfWork(block) fmt.Printf("Pow is %s\n", strconv.FormatBool(pow.Validate())) println() }}
/lession02/src/core/block.go
package coreimport ( "time" "strconv" "bytes" "crypto/sha256")type Block struct { TimeStamp int64 Data []byte PrevBlockHash []byte Hash []byte Nonce int}func NewBlock(data string, prevBlockHash []byte) *Block { block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}, 0} pow := NewProofOfWork(block) block.Nonce, block.Hash = pow.Run() return block}func (b *Block) SetHash() { strTimeStamp := []byte(strconv.FormatInt(b.TimeStamp, 10)) headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, strTimeStamp}, []byte{}) hash := sha256.Sum256(headers) b.Hash = hash[:]}func NewGenesisBlock() *Block { return NewBlock("Genesis Block", []byte{})}
/lession02/src/core/blockchain.go
package coretype BlockChain struct { Blocks []*Block}func (bc *BlockChain) AddBlock(data string) { preBlock := bc.Blocks[len(bc.Blocks)-1] newBlock := NewBlock(data, preBlock.Hash) bc.Blocks = append(bc.Blocks, newBlock)}func NewBlockChain() *BlockChain { return &BlockChain{[]*Block{NewGenesisBlock()}}}
/lession02/src/core/proofofwork.go
package coreimport ( "math" "math/big" "fmt" "crypto/sha256" "bytes")var ( maxNonce = math.MaxInt64)const targetBits = 20type ProofOfWork struct { block *Block target *big.Int}func NewProofOfWork(b *Block) *ProofOfWork { target := big.NewInt(1) target.Lsh(target, uint(256-targetBits)) pow := &ProofOfWork{b, target} return pow}func (pow *ProofOfWork) prepareData(nonce int) []byte { data := bytes.Join([][]byte{ pow.block.PrevBlockHash, pow.block.Data, IntToHex(pow.block.TimeStamp), IntToHex(int64(targetBits)), IntToHex(int64(nonce)), }, []byte{}) return data}func (pow *ProofOfWork) Run() (int, []byte) { var hashInt big.Int var hash [32]byte nonce := 0 fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data) for nonce < maxNonce { data := pow.prepareData(nonce) hash = sha256.Sum256(data) fmt.Printf("\r%x", hash) hashInt.SetBytes(hash[:]) if hashInt.Cmp(pow.target) == -1 { break } else { nonce++ } } fmt.Printf("\n\n") return nonce, hash[:]}func (pow *ProofOfWork) Validate() bool { var hashInt big.Int data := pow.prepareData(pow.block.Nonce) hash := sha256.Sum256(data) hashInt.SetBytes(hash[:]) isValid := hashInt.Cmp(pow.target) == -1 return isValid}
/lession02/src/core/utils.go
package coreimport ( "bytes" "encoding/binary" "log" "crypto/sha256")func IntToHex(num int64) []byte { buff := new(bytes.Buffer) err := binary.Write(buff, binary.BigEndian, num) if err != nil { log.Panic(err) } return buff.Bytes()}func DataToHash(data []byte) []byte { hash := sha256.Sum256(data) return hash[:]}