GO语言实现区块链POW共识算法- -pow算法实现

576 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

GO语言实现区块链POW共识算法

上一篇:# GO语言实现区块链POW共识算法- -区块定义与数据串行化

pow算法实现

上文我们实现了区块链可以计算hash,但是没有确定工作难度(只需一次就可以得到hash值),为了体现工作量,需要给矿工增加挖矿难度。

1、在block.go中增加Nonce字段

type Block struct {
   Timestamp int64  //时间戳
   Data      []byte //数据域
   PrevBlockHash []byte //前一区块hash值
   Hash     []byte   //当前区块hash
   Nonce    int64  //随机值
}

2、创建pow.go文件,定义数据结构以及挖矿难度

var(
   //Nonce循环上限
   maxNonce =math.MaxInt64
)
//难度值
const targetBits=24

//pow结构
type proOfWork struct {
   Block *Block
   target *big.Int
}

3、创建pow结构

lsh的目标是求一个挖矿难度值,是想上是将target坐移256-targeiBits位,这要基于二进制的比较思想。有兴趣的可以查阅。

func NewProfOfWork(b *Block)*proOfWork{
   //target为最终难度值
   target:=big.NewInt(1)
   //target为1向左位移256-24(挖矿难度)
   target.Lsh(target,uint(256-targetBits))
   //生成pow结构
   pow:=&proOfWork{b,target}\
   return  pow
}

4、编写挖矿逻辑

//挖矿与运行
func (pow *proOfWork) Run() (int,[]byte) {
   var hashInt big.Int
   var hash [32]byte
   nonce :=0
   fmt.Printf("pow数据:%s,maxNonce%d\n",pow.block.Data,maxNonce)
   for nonce<maxNonce {
      //数据准备
      data:=pow.prepareData(int64(nonce))
      //计算hash
      hash:=sha256.Sum256(data)
      fmt.Printf("\r%x",hash)
      hashInt.SetBytes(hash[:])
      //按字节比较,hashInt.Cmp<0代表找到目标值Nonce
      if hashInt.Cmp(pow.target)==-1{
         break
      }else {
         nonce++
      }
   }
   fmt.Print("\n\n")
   return nonce,hash[:]

}
//准备函数,使用Join完成字节切片的组合
func (pow *proOfWork) prepareData(nonce int64) []byte {
   data:=bytes.Join(
      [][]byte{
      pow.block.PrevBlockHash,
      pow.block.Data,
      Int2Hex(pow.block.Timestamp),
      Int2Hex(int64(targetBits)),
      Int2Hex(nonce),
   },
   []byte{},
   )
return data
}
//组合时需要将Int转化为[]byte
func Int2Hex(num int64) []byte {
   buff:=new(bytes.Buffer)
   binary.Write(buff,binary.BigEndian,num)
   return buff.Bytes()
}

5、提供Pow校验功能

利用当前区块生成pow结构,校验hash是否符合挖矿难度


//校验区块正确性
func (pow *proOfWork) name()bool  {
   var hashInt big.Int
   data:= pow.prepareData(pow.block.Nonce)
   hash:=sha256.Sum256(data)
   hashInt.SetBytes(hash[:])
   return hashInt.Cmp(pow.target)==-1
}

6、修改block代码

我们不再需要SetHash了,完全可以在NewBlock中完成,将挖矿成功后的hash保存到block结构体中


func NewBlock(data string, prevBlockHash []byte) *Block {
   //先构造block
   block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}, 0}
   //需要先挖矿
   
   pow := NewProfOfWork(block)
   nonce, hash := pow.Run()
   //设置hash和nonce
   block.Hash = hash
   block.Nonce =int64( nonce)
   return block
}

main.go文件中,增加打印nonce值

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.Printf("nonce:%d\n",block.Nonce)
   pow:=NewProfOfWork(block)
   fmt.Printf("Pow: %t\n", pow.Validate())
   fmt.Println()

}