比特币原理及其代码实践2-工作量证明


工作量证明(proof-of-work)

哈希计算

获得指定数据的一个哈希值的过程,就叫做哈希计算。一个哈希,就是对所计算数据的一个唯一表示。对于一个哈希函数,输入任意大小的数据,它会输出一个固定大小的哈希值。下面是哈希的几个关键特性:

  1. 无法从一个哈希值恢复原始数据。也就是说,哈希并不是加密。
  2. 对于特定的数据,只能有一个哈希,并且这个哈希是唯一的。
  3. 即使是仅仅改变输入数据中的一个字节,也会导致输出一个完全不同的哈希。

Hashcash

比特币使用 Hashcash ,它可以被分解为以下步骤:

  1. 取一些公开的数据(在比特币中,它是区块头)
  2. 给这个公开数据添加一个计数器。计数器默认从 0 开始
  3. data(数据)counter(计数器) 组合到一起,获得一个哈希
  4. 检查哈希是否符合一定的条件:
  5. 如果符合条件(前多少位是0),结束
  6. 如果不符合,增加计数器,重复步骤 3-4

实现

//挖矿难度,前24位都是0,用16进制的话则是前6位都是0
const targetBits = 24

我们并不会像比特币那样动态调整难度,就是说targetBits是常量

//构造一个pow的结构体去实现功能
//我们使用bitInt,在对比hash值的时候,转化成数字比大小
type ProofOfWork struct {
    block  *Block
    target *big.Int
}


func NewProofOfWork(b *Block) *ProofOfWork {
    target := big.NewInt(1)
    //0000010000000000000000000...
    //左移256-targetBits位
    target.Lsh(target, uint(256-targetBits))

    pow := &ProofOfWork{b, target}

    return pow
}

//准备数据
//data = PrevBlockHash + blockData(交易的hash值) + Timestamp + targetBits + nonce
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)),
        },
        []byte{},
    )
    return data
}

//pow运算
//1. hash = hash(PrevBlockHash + blockData + Timestamp + targetBits + nonce)
//2. 如果hash < target 则成功
//3. 否则 nonce++ 重复运算
func (pow *ProofOfWork) Run() (int, []byte) {
    var hashInt big.Int
    var hash [32]byte
    nonce := 0

    fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data)
    for nonce < maxNonce {
        //计算hash
        data := pow.prepareData(nonce)
        hash = sha256.Sum256(data)
        hashInt.SetBytes(hash[:])

        //这里对比hash值和目标值的大小,如果小于,则pow成功
        if hashInt.Cmp(pow.target) == -1 {
            fmt.Printf("\r%x", hash)
            break
        } else {
            nonce++
        }
    }
    fmt.Print("\n\n")

    return nonce, hash[:]
}

//pow验证
func (pow *ProofOfWork) Validate() bool {
    var hashInt big.Int

    data := pow.prepareData(pow.block.Nonce)
    hash := sha256.Sum256(data)
    hashInt.SetBytes(hash[:])

    isValid := hashInt.Cmp(pow.target) == -1

    return isValid
}

func main() {
    ...

    for _, block := range bc.blocks {
        ...
        pow := NewProofOfWork(block)
        fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate()))
        fmt.Println()
    }
}