Golang区块链开发002-初始化区块链与POW挖矿实现

目录:

一.初始化区块链

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 


猜你喜欢

转载自blog.51cto.com/clovemfong/2149933