持续创作,加速成长!这是我参与「掘金日新计划 · 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()
}