go区块链公链实战0x01ProofOfWork

上一节 我们用go语言实现了区块链的基础结构。今天来实现工作量证明。

区块新属性Nonce

我们先来看一下上节实现的区块结构:

type Block struct {
    //1.区块高度
    Height int64
    //2.上一个区块HAsh
    PrevBlockHash []byte
    //3.交易数据
    Data []byte
    //4.时间戳
    Timestamp int64
    //5.Hash
    Hash []byte
}

我们都知道一个合法区块的诞生其哈希值必须满足指定的条件,比特币采用的是工作量证明。我们这里用go开发的公链也采用POW一致性算法来产生合法性区块。

因此,区块必须不断产生哈希直到满足POW的哈希值产生才能添加到主链上成为合法区块。看看上图区块的基本结构,对于一个区块来说,1-4项属性都市固定,而区块哈希又是由这些属性拼接生成的。所以,要想让区块哈希能不断变化,必须引入一个变量Nonce。

引入Nonce后,就可以通过改变Nonce值来不断产生新的哈希值直到找到满足条件的哈希。

POW难度targetBits

前面用Python简单介绍过区块链中的挖矿概念,一般地,对于256位的哈希值来说设定挖矿条件的方式往往是:前多少位为0。targetBits便是用于指定目标哈希需满足的条件的,即计算的哈希值必须前targetBits位为0.

而对于一个256位的二进制串判断前多少位为0显得很繁琐,我们可以巧妙地通过位移运算将这一判断转换为一个数学问题。eg:

假设哈希值得位数为8,当前targetBits为2(256位亦然,用8举例是位数少便于描述),那么目标哈希值必须满足前两位都是0。从临界情况入手,当第2位不为0时的最小的数为0100 0000,只要小于这个数就是符合条件的哈希值。那么这些数是怎么找到的呢?

1.8位哈希值最小的非0值为0000 0001
2.该值左移8-targetBits位,0000 0001 << 6 = 0100 0000 = target
3.if hash < target 区块合法

Block结构完善

Nonce
type Block struct {
    //1.区块高度
    Height int64
    //2.上一个区块HAsh
    PrevBlockHash []byte
    //3.交易数据
    Data []byte
    //4.时间戳
    Timestamp int64
    //5.Hash
    Hash []byte
    //6.Nonce  符合工作量证明的随机数
    Nonce int64
}
新区块产生
//1.创建新的区块
func NewBlock(data string, height int64, prevBlockHash []byte) *Block {

    //创建区块
    block := &Block{
        Height:        height,
        PrevBlockHash: prevBlockHash,
        Data:          []byte(data),
        Timestamp:     time.Now().Unix(),
        Hash:          nil,
        Nonce:         0}

    //调用工作量证明返回有效的Hash
    pow := NewProofOfWork(block)
    hash, nonce := pow.Run()
    block.Hash = hash[:]
    block.Nonce = nonce

    fmt.Printf("\r%d-%x\n", nonce, hash)

    return block
}

ProofOfWork

基本结构
//期望计算的Hash值前面至少要有16个零
const targetBits = 16

type ProofOfWork struct {
    //求工作量的block
    Block *Block
    //工作量难度 big.Int大数存储
    target *big.Int
}
创建新的POW对象
//创建新的工作量证明对象
func NewProofOfWork(block *Block) *ProofOfWork {

    /**
    target计算方式  假设:Hash为8位,targetBit为2位
    eg:0000 0001(8位的Hash)
    1.8-2 = 6 将上值左移6位
    2.0000 0001 << 6 = 0100 0000 = target
    3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值
    */

    //1.创建一个初始值为1的target
    target := big.NewInt(1)
    //2.左移bits(Hash) - targetBit 位
    target = target.Lsh(target, 256-targetBits)

    return &ProofOfWork{block, target}
}
哈希值的预选值
//拼接区块属性,返回字节数组
func (pow *ProofOfWork) prepareData(nonce int) []byte {

    data := bytes.Join(
        [][]byte{
            pow.Block.PrevBlockHash,
            pow.Block.Data,
            IntToHex(pow.Block.Timestamp),
            IntToHex(int64(targetBits)),
            IntToHex(int64(nonce)),
            IntToHex(int64(pow.Block.Height)),
        },
        []byte{},
    )

    return data
}
区块有效性验证
//判断当前区块是否有效
func (proofOfWork *ProofOfWork) IsValid() bool  {

    //比较当前区块哈希值与目标哈希值
    var hashInt big.Int
    hashInt.SetBytes(proofOfWork.Block.Hash)

    if proofOfWork.target.Cmp(&hashInt) == 1 {

        return true
    }

    return false
}
挖矿(产生有效的哈希值)
//运行工作量证明
func (proofOfWork *ProofOfWork) Run() ([]byte, int64) {

    //1.将Block属性拼接成字节数组

    //2.生成hash
    //3.判断Hash值有效性,如果满足条件跳出循环

    //用于寻找目标hash值的随机数
    nonce := 0
    //存储新生成的Hash值
    var hashInt big.Int
    var hash [32]byte

    for {
        //准备数据
        dataBytes := proofOfWork.prepareData(nonce)
        //生成Hash
        hash = sha256.Sum256(dataBytes)

        //\r将当前打印行覆盖
        //fmt.Printf("\r%x", hash)
        //存储Hash到hashInt
        hashInt.SetBytes(hash[:])
        //验证Hash
        if proofOfWork.target.Cmp(&hashInt) == 1 {

            break
        }
        nonce++
    }

    return hash[:], int64(nonce)
}

POW测试


package main

import (
    "chaors.com/LearnGo/publicChaorsChain/part2-ProofOfWork-Prototype/BLC"
    "fmt"
)

func main() {

    //genesisBlock := BLC.CreateGenesisBlock("Genenis Block")
    //创建带有创世区块的区块链
    blockchain := BLC.CreateBlockchainWithGensisBlock()
    //添加一个新区快
    blockchain.AddBlockToBlockchain("first Block",
        blockchain.Blocks[len(blockchain.Blocks)-1].Height,
        blockchain.Blocks[len(blockchain.Blocks)-1].Hash)
    blockchain.AddBlockToBlockchain("second Block",
        blockchain.Blocks[len(blockchain.Blocks)-1].Height,
        blockchain.Blocks[len(blockchain.Blocks)-1].Hash)
    blockchain.AddBlockToBlockchain("third Block",
        blockchain.Blocks[len(blockchain.Blocks)-1].Height,
        blockchain.Blocks[len(blockchain.Blocks)-1].Hash)
    fmt.Println(blockchain)
}

运行后,consle会不断打印计算出的Hash值,直到计算出的Hash值满足条件。当我们调整POW难度targetBits,会发现计算的时间有所改变。targetBits越大,挖矿难度越大,新区快产生的时间也就越久。

源代码在这,喜欢的朋友记得给个小star,或者fork.也欢迎大家一起探讨区块链相关知识,一起进步!

.
.
.
.

互联网颠覆世界,区块链颠覆互联网!

---------------------------------------------20180623 18:13

你可能感兴趣的:(go区块链公链实战0x01ProofOfWork)