1. 程式人生 > >Golang區塊鏈開發002-初始化區塊鏈與POW挖礦實現

Golang區塊鏈開發002-初始化區塊鏈與POW挖礦實現

pow append var space [] sha2 測試結果 rep ring

目錄:

一.初始化區塊鏈

1.代碼結構

2. 定義區塊結構與方法

3. 定義區塊鏈結構與方法

4. 幫助庫代碼

5. 測試生成區塊與初始化區塊鏈

6. 測試代碼

二. POW挖礦實現

1.代碼結構

2. 定義pow算法實現

3. 修改區塊的生成方式(從自定義到挖礦)

4. 測試代碼,測試挖礦

5.驗證區塊有效性


一.初始化區塊鏈

1. 代碼結構

技術分享圖片


Block.go :定義區塊結構與方法

BlockChain.go :定義區塊鏈結構與方法

help.go :將常用代碼塊進行封裝,形成幫助庫

main.go:測試代碼


2.定義區塊結構與方法


package BLC

import (
   "time"
   "strconv"
   "bytes"
   "crypto/sha256"
)

//定義區塊
type Block struct {
   //1.區塊高度,也就是區塊的編號,第幾個區塊
   Height int64
   //2.上一個區塊的Hash值
   PreBlockHash []byte
   //3.交易數據(最終都屬於transaction 事務)
   Data []byte
   //4.創建時間的時間戳
   TimeStamp int64
   //5.當前區塊的Hash值
   Hash []byte
   //6.Nonce 隨機數,用於驗證工作量證明
   Nonce int64
}

//定義區塊生成Hash的方法
func (block *Block) SetHash() {
   //1.將Height 轉換為字節數組 []byte
   heightBytes := IntToHex(block.Height)

   //2.將TimeStamp 轉換為字節數組 []byte
   //2.1 將Int64的TimeStamp 轉換成二進制
   timeString := strconv.FormatInt(block.TimeStamp, 2)
   //2.2 將二進制字符串轉成字節數組
   timeBytes := []byte(timeString)

   //3.拼接所有屬性,形成一個二維的byte數組
   blockBytes := bytes.Join([][]byte{heightBytes, block.PreBlockHash, block.Data, timeBytes, block.Hash}, []byte{})
   //4.生成Hash
   hash := sha256.Sum256(blockBytes)
   block.Hash = hash[:]
}

//1. 創建新的區塊
func NewBlock(data string, height int64, PreBlockHash []byte) *Block {
   //創建區塊
   block := &Block{
      height,
      PreBlockHash,
      []byte(data),
      time.Now().Unix(),
      nil,
      0,
   }
   //設置Hash
   block.SetHash()
   return block

}

//2.生成創世區塊
func CreateGenesisBlock(data string) *Block {

   return NewBlock(data, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})

}



3.定義區塊鏈與方法

package BLC

type BlockChain struct {
   Blocks []*Block //存儲有序的區塊
}


func (blc *BlockChain)AddBlockChain(data string,height int64,preHash []byte){
   //創建新區塊
   newBlock := NewBlock(data,height,preHash)
   //往鏈中添加區塊
   blc.Blocks=append(blc.Blocks,newBlock)

}


//1.創建帶有創世區塊的區塊鏈
func CreateBlockChainWithGenesisBlock() *BlockChain {

   //創建創世區塊
   genesisBlock := CreateGenesisBlock("Genesis Data..")
   //返回區塊鏈對象
   return &BlockChain{[]*Block{genesisBlock}}

}


4.幫助代碼庫

package BLC

import (
   "bytes"
   "encoding/binary"
   "log"
)

//將int64轉換為字節數組
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()
}


5.測試代碼

package main

import (
   "publicChain/BLC"
   "fmt"
)

func main() {

   //創建創世區塊
   blockChain := BLC.CreateBlockChainWithGenesisBlock()

   //創建新的區塊
   blockChain.AddBlockChain("Send $100 to Bruce", blockChain.Blocks[len(blockChain.Blocks)-1].Height+1, blockChain.Blocks[len(blockChain.Blocks)-1].Hash)
   blockChain.AddBlockChain("Send $200 to Apple", blockChain.Blocks[len(blockChain.Blocks)-1].Height+1, blockChain.Blocks[len(blockChain.Blocks)-1].Hash)
   blockChain.AddBlockChain("Send $300 to Alice", blockChain.Blocks[len(blockChain.Blocks)-1].Height+1, blockChain.Blocks[len(blockChain.Blocks)-1].Hash)
   blockChain.AddBlockChain("Send $400 to Bob", blockChain.Blocks[len(blockChain.Blocks)-1].Height+1, blockChain.Blocks[len(blockChain.Blocks)-1].Hash)

   fmt.Printf("創建的區塊鏈為:\t%v\n", blockChain)
   fmt.Printf("區塊鏈存儲的區塊為:\t%v\n", blockChain.Blocks)
   fmt.Printf("第二個區塊的數據信息(交易信息)為:\t%v\n", string(blockChain.Blocks[1].Data))

}


結果顯示


二. POW挖礦實現

1.代碼結構


技術分享圖片

多出的ProofOfWork.go用於實現挖礦


2. 定義pow算法實現

ProofOfWork.go


package BLC

import (
   "math/big"
   "bytes"
   "crypto/sha256"
   "fmt"
   "time"
)

type ProofOfWork struct {
   Block  *Block   //當前要驗證的區塊
   target *big.Int //大數存儲,區塊難度
}

//數據拼接,返回字節數組
func (pow *ProofOfWork) prePareData(nonce int) []byte {

   data := bytes.Join(
      [][]byte{
         pow.Block.PreBlockHash,
         pow.Block.Data,
         IntToHex(pow.Block.TimeStamp),
         IntToHex(int64(targetBit)),
         IntToHex(int64(nonce)),
         IntToHex(int64(pow.Block.Height)),
      },
      []byte{},
   )
   return data
}

//256位Hash裏面至少要有16個零0000 0000 0000 0000
const targetBit = 16

func (proofOfWork *ProofOfWork) Run(num int64) ([]byte, int64) {

   //3.判斷Hash的有效性,如果滿足條件循環體

   nonce := 0
   var hashInt big.Int //存儲新生成的hash值
   var hash [32]byte

   for {
      //1. 將Block的屬性拼接成字節數組
      databytes := proofOfWork.prePareData(nonce)

      //2.生成Hash
      hash = sha256.Sum256(databytes)
      fmt.Printf("挖礦中..%x\n", hash)
      //3. 將hash存儲至hashInt
      hashInt.SetBytes(hash[:])


      //4.判斷hashInt是否小於Block裏面的target
      // Cmp compares x and y and returns:
      //
      //   -1 if x <  y
      //    0 if x == y
      //   +1 if x >  y
      //需要hashInt(y)小於設置的target(x)
      if proofOfWork.target.Cmp(&hashInt) == 1 {
         //fmt.Println("挖礦成功", hashInt)
         fmt.Printf("第%d個區塊,挖礦成功:%x\n",num,hash)
         fmt.Println(time.Now())
         time.Sleep(time.Second * 2)
         break

      }

      nonce ++

   }

   return hash[:], int64(nonce)

}

//創建新的工作量證明對象
func NewProofOfWork(block *Block) *ProofOfWork {
   /*1.創建初始值為1的target
   0000 0001
   8 - 2
   */

   target := big.NewInt(1)

   //2.左移256-targetBit
   target = target.Lsh(target, 256-targetBit)

   return &ProofOfWork{block, target}
}




3. 修改區塊的生成方式(從自定義到挖礦)

Block.go

package BLC

import (
   "time"
)

//定義區塊
type Block struct {
   //1.區塊高度,也就是區塊的編號,第幾個區塊
   Height int64
   //2.上一個區塊的Hash值
   PreBlockHash []byte
   //3.交易數據(最終都屬於transaction 事務)
   Data []byte
   //4.創建時間的時間戳
   TimeStamp int64
   //5.當前區塊的Hash值
   Hash []byte
   //6.Nonce 隨機數,用於驗證工作量證明
   Nonce int64
}

//1. 創建新的區塊
func NewBlock(data string, height int64, PreBlockHash []byte) *Block {
   //創建區塊
   block := &Block{
      height,
      PreBlockHash,
      []byte(data),
      time.Now().Unix(),
      nil,
      0,
   }
   //調用工作量證明的方法,並且返回有效的Hash和Nonce值
   //創建pow對象
   pow := NewProofOfWork(block)
   //挖礦驗證
   hash, nonce := pow.Run(height)

   block.Hash = hash[:]
   block.Nonce = nonce
   return block

}

//2.生成創世區塊
func CreateGenesisBlock(data string) *Block {

   return NewBlock(data, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})

}


4. 測試代碼,測試挖礦

main.go


package main

import (
   "publicChain/part2-工作量證明/BLC"
   "fmt"
)

func main() {

   fmt.Println("開始挖礦")
   //創建創世區塊
   blockChain := BLC.CreateBlockChainWithGenesisBlock()

   //創建新的區塊
   blockChain.AddBlockChain("Send $100 to Bruce", blockChain.Blocks[len(blockChain.Blocks)-1].Height+1, blockChain.Blocks[len(blockChain.Blocks)-1].Hash)

   blockChain.AddBlockChain("Send $200 to Apple", blockChain.Blocks[len(blockChain.Blocks)-1].Height+1, blockChain.Blocks[len(blockChain.Blocks)-1].Hash)

   blockChain.AddBlockChain("Send $300 to Alice", blockChain.Blocks[len(blockChain.Blocks)-1].Height+1, blockChain.Blocks[len(blockChain.Blocks)-1].Hash)

   blockChain.AddBlockChain("Send $400 to Bob", blockChain.Blocks[len(blockChain.Blocks)-1].Height+1, blockChain.Blocks[len(blockChain.Blocks)-1].Hash)

   fmt.Printf("創建的區塊鏈為:\t%v\n", blockChain)
   fmt.Printf("區塊鏈存儲的區塊為:\t%v\n", blockChain.Blocks)
   fmt.Printf("第二個區塊的數據信息(交易信息)為:\t%v\n", string(blockChain.Blocks[1].Data))
   fmt.Printf("第二個區塊的隨機數為:\t%v\n", blockChain.Blocks[1].Nonce)

}

測試結果

技術分享圖片


共計對五個區塊進行挖礦,結果如上

5.驗證區塊有效性

ProofOfWork.go


//判斷挖礦得到的區塊是否有效
func (proofOfWork *ProofOfWork) IsValid() bool {
   //1.proofOfWork.Block.Hash
   //2.proofOfWork.Target
   var hashInt big.Int

   hashInt.SetBytes(proofOfWork.Block.Hash)

   if proofOfWork.target.Cmp(&hashInt) == 1 {
      return true
   }
   return false
}

測試代碼:

main.go

//通過POW挖出新的區塊block
block := BLC.NewBlock("Send $500 to Tom", blockChain.Blocks[len(blockChain.Blocks)-1].Height+1, blockChain.Blocks[len(blockChain.Blocks)-1].Hash)
//手動將該區塊添加至區塊鏈中
blockChain.Blocks = append(blockChain.Blocks, block)
//創建一個工作量證明對象
proofOfWork := BLC.NewProofOfWork(block)
//判斷該區塊是否合法有效
fmt.Println(proofOfWork.IsValid())

測試結果:


技術分享圖片

第六個區塊是我們新創建的區塊,返回值為true,驗證有效


參考資料:

區塊鏈共識算法-POW: https://www.jianshu.com/p/b23cbafbbad2


Golang區塊鏈開發002-初始化區塊鏈與POW挖礦實現