工作量证明机制和工作量验证机制
在这一步加入工作量证明机制和工作量验证机制
实现工作量证明
定义实现工作量证明的结构
//实现挖矿功能 pow
// 字段:
// 区块:block
// 目标值:target
// 方法:
// run计算
// 功能:找到nonce,从而满足哈希币目标值小
type ProofOfWork struct {
// 区块:block
block *Block
// 目标值:target,这个目标值要与生成哈希值比较
target *big.Int //结构,提供了方法:比较,把哈希值设置为big.Int类型
}
定义对应的初始化函数
//创建ProofOfWork
//block由用户提供
//target目标值由系统提供
func NewProofOfWork(block *Block) *ProofOfWork {
pow := ProofOfWork{
block: block,
}
//难度值先写死,不去推导,后面补充推导方式
targetStr := "0000100000000000000000000000000000000000000000000000000000000000"
tmpBigInt := new(big.Int)
//将我们的难度值赋值给bigint,是16进制,4位可以确定一个16进制数,所以我们需要64个16进制数,这样正好256位
tmpBigInt.SetString(targetStr, 16)
pow.target = tmpBigInt
return &pow
}
因为要进行hash运算,定义一个准备数据的方法
//拼接nonce和block数据
func (pow *ProofOfWork) PrepareData(nonce uint64) []byte {
b := pow.block
tmp := [][]byte{
uintToByte(b.Version), //将uint64转换为[]byte
b.PrevHash,
b.MerkleRoot,
uintToByte(b.TimeStamp),
uintToByte(b.Bits),
uintToByte(nonce),
//uintToByte(b.Nonce), <<<<=====不要忘记修改,否则nonce不会产生作用
//b.Hash, <<<<=====不要忘记区块本身的hash不应该参与hash运算,否则此时hash为nil,之后不为nil,验证时肯定会不同
b.Data,
}
//使用join方法,将二维切片转为1维切片
data := bytes.Join(tmp, []byte{})
return data
}
定义Run方法
开始挖矿吧
//挖矿函数,不断变化nonce,使得sha256(数据+nonce) < 难度值
//返回:区块哈希,nonce
func (pow *ProofOfWork) Run() ([]byte, uint64) {
//定义随机数
var nonce uint64
var hash [32]byte
//这里定义长度为32的数组,是因为,一个字节是8位,32字节正好是256位
for {
// 1. 拼接字符串 + nonce
data := pow.PrepareData(nonce)
// 2. 哈希值 = sha256(data)
hash = sha256.Sum256(data)//[32]byte
//将hash转换为bigInt类型
tmpInt := new(big.Int)
tmpInt.SetBytes(hash[:])
// -1 if x < y
// 0 if x == y
// +1 if x > y
//当前计算的哈希.Cmp(难度值)
if tmpInt.Cmp(pow.target) == -1 {
fmt.Printf("挖矿成功,hash :%x, nonce :%d\n", hash[:], nonce)
break
} else {
//如果不小于难度值
nonce++
}
} //for, 如果嵌套层级过多,可以使用在闭合}位置写上嵌套的类型
// return 哈希,nonce
return hash[:], nonce
}
验证区块
- 获取区块
- 拼装数据(block + nonce)
- 计算sha256
- 与难度值比较
定义一个验证区块的方法
func (pow *ProofOfWork) IsValid() bool {
// 1. 获取区块
// 2. 拼装数据(block + nonce)
data := pow.PrepareData(pow.block.Nonce)
// 3. 计算sha256
hash := sha256.Sum256(data)
// 4. 与难度值比较
tmpInt := new(big.Int)
tmpInt.SetBytes(hash[:])
// if tmpInt.Cmp(pow.target) == -1 {
// return true
// }
// return false
//满足条件,返回true
return tmpInt.Cmp(pow.target) == -1
}
可以看出来,虽然在生成哈希值的时候是很麻烦的,但是在验证的时候却非常方便,只需一次运算。
集成到整体结构
此时在创建新区块的时候应该加上工作量证明机制
func NewBlock(data string, prevHash []byte) *Block {
b := Block{
...
}
//计算哈希值
// b.setHash()
//将POW集成到Block中
pow := NewProofOfWork(&b)
hash, nonce := pow.Run()
b.Hash = hash
b.Nonce = nonce
return &b
}
在遍历区块链上的区块数据时,加上验证的步骤
func main() {
bc := NewBlockChain()
...
for i, block := range bc.Blocks {
...
//验证区块
pow := NewProofOfWork(block)
fmt.Printf("IsValid: %v\n", pow.IsValid())
}
}