经典共识PoW的原理及实现

经典共识PoW的原理及实现

一、PoW简介

PoW(Proof of Work)是工作量证明的简称,最早使用在防止拒绝服务攻击以及过滤垃圾邮件,现在成为区块链公链中最常见也是最有效的共识算法之一,当下最流行的比特币、以太坊等公链目前均使用PoW共识。

​ PoW是通过对一个复杂难题的求解,来保证区块链交易打包出块的公平性,即最先解决难题的矿工,可以获得记账权,并将打包好的区块发送至其他节点验证上链,从而获得激励。在保证能够在不可信的环境下创造可信的同时,运用PoW还可以天然的防御女巫攻击等针对区块链的攻击。

​ 由于PoW要求所有矿工节点都解决无意义的复杂难题,导致了巨大的资源浪费,因此,最新的以太坊版本考虑使用PoS代替PoW成为其共识算法。

二、PoW原理及实现

​ 在介绍PoW原理之前,需要先了解一些知识。

2.1 区块头

​ 在区块链中,区块分为区块头和区块体两部分,区块体以默克尔树的数据结构存储了交易数据,区块头存储了上一个区块的Hash、默克尔树树根值、时间戳、难度值、区块高度等等非交易数据信息。

2.2 哈希计算

​ 哈希计算实际上就是使用哈希函数(散列函数)对不同长度的数据都能计算出等长的输出,而且对数据微小差距都十分敏感,比如"hello world"和"Hello world"的哈希值差距都是巨大的。

2.3 原理及实现

​ PoW的原理十分简单,实际上就是计算一个随机数Nounce,要求这个Nounce和区块头拼接后做的哈希值小于我们预设的值。具体实现如下:

​ 1.预设一个难度值targetBit,比如我们想PoW计算出的Hash值最前面开始有16个0,则设置该值为16。

​ 2.找到判断是否找出正确Nounce值的临界值target,在比特币中,PoW使用的是SHA256,输出的哈希值都是256位的,比如设置的难度值是16,那么我们最终应该得到的值的形式应该是 0000 0000 0000 0000 xxxx…xxxx(共256位),那么我们可以得知,当我得到的值小于0000 0000 0000 0001 0000…0000(共256位)时,该值合法,因为这个值已经小于了前面有15个0的最小值,那么其前面一定有16个0。

​ 所以我们只要将临界值target先设置为1,二进制表示为0000 0000…0001(共256位),再将这个值左移256-targetBit = 256 - 16 = 240位,即可得到前面有15个0的最小值,即0000 0000 0000 0001 0000…0000(共256位)。

​ 3.将随机数Nounce置为0,再拼接上区块头的内容,计算其哈希值,如果大于了我们的目标值target,则将Nounce加1,再拼接区块头的内容计算哈希。重复上述过程,直到计算出小于target的哈希值,此时的Nounce就是最终的解。

三、PoW的go语言实现

1.首先定义工作量证明对象

type ProofOfWork struct {
   Block  *Block   //当前要验证的区块
   target *big.Int //大数存储
}

2.定义难度值

//256位hash里面至少有16个零
const targetBit = 16

3.初始化工作量证明对象,计算出临界值target

func NewProofOfWork(block *Block) *ProofOfWork {
   //创建一个初始值为1的target
   target := big.NewInt(1)
   //左移256-targetBit
   target = target.Lsh(target, 256-targetBit)
   return &ProofOfWork{
      block,
      target,
   }
}

4.拼接Nounce值和区块头中的内容

func (proofOfWork *ProofOfWork) prepareData(nonce int) []byte {
   join := bytes.Join(
      [][]byte{
         proofOfWork.Block.PreBlockHash,//前一个区块的hash
         proofOfWork.Block.HashTransactions(),//默克尔树根
         Utils.IntToHex(proofOfWork.Block.TimeStamp),//时间戳
         Utils.IntToHex(int64(targetBit)),//难度值
         Utils.IntToHex(int64(nonce)),//Nounce
         Utils.IntToHex(int64(proofOfWork.Block.Height)),//区块高度
      },
      []byte{},
   )
   return join
}

5.开始进行工作量证明计算

func (proofOfWork *ProofOfWork) Run() ([]byte, int64) {

   nonce := 0
   var hashInt big.Int //存储我们新生成的hash
   var hash [32]byte
   for {
      // 将BLOCK属性拼接成字节数组
      dataBytes := proofOfWork.prepareData(nonce)
      // 生成hash, sum256返回32位需要转换为64位
      hash = sha256.Sum256(dataBytes)
      // 将hash存储到hashInt,采取hash[:]将切片转换为64位
      hashInt.SetBytes(hash[:])
      fmt.Printf("\r%x", hash)
      // 判断hashInt是否小于Block里面的target
      // x < y -1
      // x == y 0
      // x > y 1
      if proofOfWork.target.Cmp(&hashInt) == 1 {
         //判断有效性,如果满足条件,跳出循环
         break
      }
      nonce = nonce + 1
   }
   return hash[:], int64(nonce)
}

6.编写当需要验证Nounce值是否合法时的验证函数

func (proofOfWork *ProofOfWork) IsValid() bool {
	var hashInt big.Int
	hashInt.SetBytes(proofOfWork.Block.Hash)
  //判断提供的hash是否小于target
	if proofOfWork.target.Cmp(&hashInt) == 1 {
		return true
	}
	return false
}
上一篇:50. Pow(x, n)


下一篇:实验2-4-2 生成3的乘方表 (15 分)