v2版本思路

1. POW介绍
– 定义一个工作量证明的结构ProofOfWork
a. block
b. 目标值

2. 提供创建POW的函数
– NewProofOfWork(参数)

3. 提供计算不断计算hash的哈数
– Run()

4. 提供一个校验函数
– IsValid()

POW定义

type ProofOfWork struct {
	block *Block

	//来存储哈希值,它内置一些方法Cmp:比较方法
	// SetBytes : 把bytes转成big.int类型 []byte("0x00000919011eeb8fbdf0c476d8510b8e1e632eba7b584ac04c11ad20cbbdd394")
	// SetString : 把string转成big.int类型 "0x00000919011eeb8fbdf0c476d8510b8e1e632eba7b584ac04c11ad20cbbdd394"
	target *big.Int //系统提供的,是固定的
}

func NewProofOfWork(block *Block) *ProofOfWork {
	pow := ProofOfWork{
		block: block,
	}

	//写难度值,难度值应该是推导出来的,但是我们为了简化,把难度值先写成固定的,一切完成之后,再去推导
	// 0000100000000000000000000000000000000000000000000000000000000000

	//16制格式的字符串
	targetStr := "0000100000000000000000000000000000000000000000000000000000000000"
	var bigIntTmp big.Int
	bigIntTmp.SetString(targetStr, 16)

	pow.target = &bigIntTmp

	return &pow
}

RUN函数实现

//这是pow的运算函数,为了获取挖矿的随机数,同时返回区块的哈希值
func (pow *ProofOfWork) Run() ([]byte, uint64) {
	//1. 获取block数据
	//2. 拼接nonce
	//3. sha256
	//4. 与难度值比较
	//a. 哈希值大于难度值,nonce++
	//b. 哈希值小于难度值,挖矿成功,退出

	var nonce uint64

	//block := pow.block

	var hash [32]byte

	for ; ; {

		//data := block + nonce
		hash = sha256.Sum256(pow.prepareData(nonce))

		//将hash(数组类型)转成big.int, 然后与pow.target进行比较, 需要引入局部变量
		var bigIntTmp big.Int
		bigIntTmp.SetBytes(hash[:])

		//   -1 if x <  y
		//    0 if x == y
		//   +1 if x >  y
		//
		//func (x *Int) Cmp(y *Int) (r int) {
		//   x              y
		if bigIntTmp.Cmp(pow.target) == -1 {
			//此时x < y , 挖矿成功!
			fmt.Printf("挖矿成功!nonce: %d, 哈希值为: %x\n", nonce, hash)
			break
		} else {
			nonce ++
		}
	}

	return hash[:], nonce
}

func (pow *ProofOfWork) prepareData(nonce uint64) []byte {
	block := pow.block

	tmp := [][]byte{
		uintToByte(block.Version),
		block.PrevBlockHash,
		block.MerKleRoot,
		uintToByte(block.TimeStamp),
		uintToByte(block.Difficulity),
		block.Data,
		uintToByte(nonce),
	}

	data := bytes.Join(tmp, []byte{})
	return data
}

使用pow更新NewBlock

//创建区块,对Block的每一个字段填充数据即可
func NewBlock(data string, prevBlockHash []byte) *Block {
	block := Block{
		Version: 00,

		PrevBlockHash: prevBlockHash,

		MerKleRoot: []byte{},

		TimeStamp: uint64(time.Now().Unix()),

		Difficulity: 10, //随便写的,v2再调整

		Nonce: 10, //同Difficulty

		Data: []byte(data),

		Hash: []byte{}, //先填充为空,后续会填充数据
	}

	//block.SetHash()
	pow := NewProofOfWork(&block)
	hash, nonce := pow.Run()

	block.Hash = hash
	block.Nonce = nonce

	return &block
}

校验挖矿是否有效

func (pow *ProofOfWork) IsValid() bool {
	//在校验的时候,block的数据是完整的,我们要做的是校验一下,Hash,block数据,和Nonce是否满足难度值要求

	//获取block数据
	//拼接nonce
	//做sha256
	//比较

	//block := pow.block
	data := pow.prepareData(pow.block.Nonce)
	hash := sha256.Sum256(data)

	var tmp big.Int
	tmp.SetBytes(hash[:])

	//if tmp.Cmp(pow.target) == -1 {
	//	return true
	//}
	// return false

	return tmp.Cmp(pow.target) == -1
}

打印block字段

	for i, block := range bc.Blocks {
		fmt.Printf("+++++++++++++++ %d ++++++++++++++\n", i)
		fmt.Printf("Version : %d\n", block.Version)
		fmt.Printf("PrevBlockHash : %x\n", block.PrevBlockHash)
		fmt.Printf("MerKleRoot : %x\n", block.MerKleRoot)

		timeFormat := time.Unix(int64(block.TimeStamp), 0).Format("2006-01-02 15:04:05")
		fmt.Printf("TimeStamp : %s\n", timeFormat)

		fmt.Printf("Difficulity : %d\n", block.Difficulity)
		fmt.Printf("Nonce : %d\n", block.Nonce)
		fmt.Printf("Hash : %x\n", block.Hash)
		fmt.Printf("Data : %s\n", block.Data)

		pow := NewProofOfWork(block)
		fmt.Printf("IsValid: %v\n", pow.IsValid())
	}

使用Bits推导难度值

const Bits = 20

func NewProofOfWork(block *Block) *ProofOfWork {
	pow := ProofOfWork{
		block: block,
	}

	//写难度值,难度值应该是推导出来的,但是我们为了简化,把难度值先写成固定的,一切完成之后,再去推导
	// 0000100000000000000000000000000000000000000000000000000000000000

	//固定难度值
	//16制格式的字符串
	//targetStr := "0001000000000000000000000000000000000000000000000000000000000000"
	//var bigIntTmp big.Int
	//bigIntTmp.SetString(targetStr, 16)
	//
	//pow.target = &bigIntTmp

	//程序推导难度值, 推导前导为3个难度值
	// 0001000000000000000000000000000000000000000000000000000000000000
	//初始化
	//  0000000000000000000000000000000000000000000000000000000000000001
	//向左移动, 256位
	//1 0000000000000000000000000000000000000000000000000000000000000000
	//向右移动, 四次,一个16进制位代表4个2进制(f:1111)
	//向右移动16位
	//0 0001000000000000000000000000000000000000000000000000000000000000

	bigIntTmp := big.NewInt(1)
	//bigIntTmp.Lsh(bigIntTmp, 256)
	//bigIntTmp.Rsh(bigIntTmp, 16)

	bigIntTmp.Lsh(bigIntTmp, 256 - Bits)

	pow.target = bigIntTmp

	return &pow
}