《区块链宝典》PoW算法、比特币挖矿原理及GO实现挖矿验证

一、挖矿原理

(一)区块头信息分析
数据分析 目的 更新时间 大小
Version版本 区域版本号 更新软件后,指定一个新的版本号 4
hashPrevBlock前一区块的HASH 前一区块的256位HASH值 新的区块进来时 32
hashMerkleRoot 默克尔根节点的HASH值 基于一个区块中所有交易的256位HASH值 接受一个交易时 32
Time时间戳 从1970-01-01 00:00 UTC开始到现在,以秒为单位的时间戳 每几秒就更新 4
Bits当前目标的HASH值 压缩格式的当前目标的HASH值 当挖矿难度调整时 4
Nonce随机数 从0开始的32位随机 产生HASH时A(每次产生HASH随机数要增长) 4
(二)挖矿的本质
  1. 挖矿是比特币共识机制中的PoW算法(工作量证明机制)
  2. 挖矿的本质就是计算哈希值
  3. 挖矿的过程就是重复计算区块头的Hash值,不断修改随机数Nonce,直到该Hash值小于目标难度bits所计算出来的哈希就算挖矿成功
    - 将区块头的6个信息拼凑在一起循环计算Hash值
    - for循环的条件是Nonce从0到int的最大值(约为40亿)。利用时间戳和Nonce值得变化,计算出一个高位为0的数值。因为目标Hash值就是一个由多个0开头的64位数字。高位0的位数越多,那么小于目标难度的Hash的可能性就越大。当小于时,即挖矿成功。
    - bits值越小,目标Hash值中的高位0就越多,这就导致每个节点要进行数以亿次的计算,才可以找到满足条件的Hash,简而言之,bits值越小,难度就越大
    - 比特币系统会调整目标的Hash值,以达到控制难度的目的。

计算难度目标及比特币bits变化趋势

计算难度目标举例说明

以区块516532为例

  1. Bits = “0x17502ab7”
    - bits是用来储存目标难度的16进制数值
  2. coefficient系数,coeffcient = 0x502ab7
  3. exponent指数,exponent = 0x17
  4. target = confficient * Math.pow(2,8*(exponent - 3))
快速计算难度目标的步骤
  1. 获取目标难度中的系数
  2. 获取目标难度中的指数
  3. 计算高位补0
  4. 计算低位补0
  5. 计算目标数值

我们平常读写,按照习惯是从做至右,左侧是大数,右侧是小数。计算机储存数据的方式是小头位序储存方式,即低位在前,高位在后。所以区块文件中记录的数据都是小头位序排列的数据。因此,在解析16进制数值时,要进行大头位序和小头位序重新排列后在进行计算。

  • 经过两次hash256算法
  • 将16进制大小端进行颠倒

三 GO 实现区块链PoW算法

package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"math/big"
	"strconv"
	"strings"
	"time"
)

func main()  {
	CheckMiningResult()
}

//验证挖矿结果
func CheckMiningResult()  {
	//初始化区块头信息
	var strVersion,strPre_hash,strMerkle_root,strTimeStamp,strBits,strNonce string
		strVersion = "20000000"
		strPre_hash = "0000000000000000002805302293df911371f53a595b2cb571c2536ceef62e60"
		strMerkle_root = "2ce0a3d3ed22dfd8ab342f5692a51e4b3a2d7778ac0e12f75fe856c59890a8a9"
		currenttime := "2018-04-26 18:40:17"
		bits := 390680589
		nonce := 3778663653

		strTimeStamp = DataToTimeHexStr(currenttime)
		fmt.Println("strTimeStamp",strTimeStamp)
		strBits = strconv.FormatInt(int64(bits),16)
		fmt.Println("strBits",strBits)
		strNonce = strconv.FormatInt(int64(nonce),16)
		fmt.Println("strNonce",strNonce)
	//计算挖矿难度目标hash
	targetHash := GetTargetHash(strBits)
	fmt.Println("目标hash",targetHash)
	//计算实际挖矿hash
	miningHash := GetMiningHash(strVersion,strPre_hash,strMerkle_root,strTimeStamp,strNonce,strBits)
	fmt.Println("实际hash",miningHash)
	//比较hash大小
	flag := CompareHash(miningHash,targetHash)
	fmt.Println("挖矿结果",flag)
}

//计算挖矿目标的hash
func GetTargetHash(bits string) string {
	//1. 获取目标难度中的系数
	coefficient := bits[2:]
	//2. 获取目标难度中的指数
	exponent := bits[:2]
	exp,_ := strconv.ParseInt(exponent,16,0)
	//3. 计算高位补0
	prefix := strings.Repeat("0",64 - int(exp) * 2)
	//4. 计算低位补0
	suffix := strings.Repeat("0",64-len(prefix) - 6)
	//5. 计算目标数值
	strTargetHash := prefix + coefficient + suffix
	return strTargetHash
}

//计算实际挖矿的hash
func GetMiningHash(strVersion,strPre_hash,strMerkle_root,strTimeStamp,strBits,strNonce string) string {
	//获取区块头中六个参数的十六进制数,转换成小头位序排列方式
	_, version := ReverseHexString(strVersion)
	_, pre_hash := ReverseHexString(strPre_hash)
	_, merkle_root := ReverseHexString(strMerkle_root)
	_, timeStamp := ReverseHexString(strTimeStamp)
	_, bits := ReverseHexString(strBits)
	_, nonce := ReverseHexString(strNonce)
	//将6个小头位序的数值拼接,形成区块头信息字符串
	header_hex := version + pre_hash + merkle_root + timeStamp + bits + nonce
	//将区块头信息经过两次hash256算法
	hashedStr := SHA256DOUSTR(header_hex,true)
	//将十六进制字符串进行大小端颠倒
	_,res := ReverseHexString(hashedStr)
	return res
}

//比较目标hash与挖矿hash的大小
func CompareHash(stringMiningHash,strTargetHash string) bool {
	var miningHash big.Int
	var targetHash big.Int
	miningHash.SetString(stringMiningHash,16)
	targetHash.SetString(strTargetHash,16)

	result := new(big.Int)
	result.Sub(&miningHash,&targetHash)
	value := fmt.Sprintf("%+d",result)
	if strings.HasPrefix(value,"-") {
		return  true
	} else if strings.HasPrefix(value,"+0"){
		return  true
	} else {
		return  false
	}
}

//将16进制进行大小端颠倒
func ReverseHexString(hexString string) ([]byte,string) {
	data,_ := hex.DecodeString(hexString)
	for i,j := 0,len(data) - 1;i < j;i,j = i + 1,j - 1 {
		data[i],data[j] = data[j],data[i]
	}
	return data,fmt.Sprintf("%x",data)
}

func SHA256DOUSTR(text string,ishex bool)string {
	a := SHA256double(text,ishex)
	return fmt.Sprintf("%x",a)
}

func ReverseByte(data[]byte)  {
	for i,j := 0, len(data) - 1 ; i < j ; i,j = i + 1,j - 1  {
		data[i],data[j] = data[j],data[i]
		//fmt.Println(i,j)
		//break
	}
}

func DateToTimeStamp(_data string) int64 {
	base_data := "2006-01-02 15:04:05"
	t,_ := time.Parse(base_data,_data)
	tm := t.Unix()
	return  tm
}

func DataToTimeHexStr(_data string) string {
	timeStampInt := DateToTimeStamp(_data)
	strTimeStamp := strconv.FormatInt(int64(timeStampInt),16)
	return strTimeStamp
}

func SHA256double(text string,ishex bool) []byte {
	hashInstance := sha256.New()
	if ishex {
		arr,_ := hex.DecodeString(text)
		hashInstance.Write(arr)
	}else {
		hashInstance.Write([]byte(text))
	}
	bytes := hashInstance.Sum(nil)
	hashInstance.Reset()
	hashInstance.Write(bytes)
	bytes = hashInstance.Sum(nil)
	return bytes
}

在这里插入图片描述

发布了24 篇原创文章 · 获赞 97 · 访问量 3520

猜你喜欢

转载自blog.csdn.net/qq_45828877/article/details/104488882