比特币挖矿算法

目录

    • 1 基础数据
    • 2 挖矿算法
    • 3 实例分析
    • 4 挖矿过程为什么要计算两次哈希值?
    • 5 参考资料

比特币目前使用的共识机制是POW,使用的挖矿算法是SHA2-256。比特币白皮书中的POW算法灵感来自于Hashcash。Hashcash的主要目的也是为了预防垃圾邮件和DDOS攻击。

1 基础数据

发行总量:2100万。
新区块生成周期:约10分钟。
挖矿难度调整周期:每2016个区块,大约2个星期。
挖矿奖励:比特币的挖矿奖励来源于两部分:

  • 创世区块奖励50个比特币,以后每210000个区块减半,即约4年调整一次。目前已经经历了两次减半,当前的挖矿奖励为12.5个比特币。
  • 比特币的每个交易必须支付一定数额的手续费给矿工。这个设定是为了防止恶意节点发送大量的垃圾交易对比特币网络进行DOS攻击。

2 挖矿算法

挖矿参考算法:挖矿算法为SHA256。在挖矿过程中,矿工将比特币的80个字节长度的区块头数据进行两次SHA256运算,运算结果就是一个256位(32字节)长度的字符串。通过比较与当前难度值的大小判断当前区块是否合法。即满足下列条件:
SHA256(SHA256(block_header))< difficulty
如果不满足上面的条件,则需要在区块头中改变一下随机值,或者使用随机数据填充coinbase交易,这样就能改变区块头的数据,从而找到满足条件的区块。这就是PoW机制的精髓所在,使用单向函数,迫使矿工不断地尝试随机数找到符合条件的区块以完成一定的计算量,保障系统的安全稳定。

3 实例分析

为了更加深入理解比特币的挖矿算法,以一个实际的区块数据为例。

首先获取区块号为100000的区块原始数据,可以在https://webbtc.com/block/000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506.hex上获取:

0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f337221b4d4c86041b0f2b57100401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020602ffffffff0100f2052a010000004341041b0e8c2567c12536aa13357b79a073dc4444acb83c4ec7a0e2f99dd7457516c5817242da796924ca4e99947d087fedf9ce467cb9f7c6287078f801df276fdf84ac000000000100000001032e38e9c0a84c6046d687d10556dcacc41d275ec55fc00779ac88fdf357a187000000008c493046022100c352d3dd993a981beba4a63ad15c209275ca9470abfcd57da93b58e4eb5dce82022100840792bc1f456062819f15d33ee7055cf7b5ee1af1ebcc6028d9cdb1c3af7748014104f46db5e9d61a9dc27b8d64ad23e7383a4e6ca164593c2527c038c0857eb67ee8e825dca65046b82c9331586c82e0fd1f633f25f87c161bc6f8a630121df2b3d3ffffffff0200e32321000000001976a914c398efa9c392ba6013c5e04ee729755ef7f58b3288ac000fe208010000001976a914948c765a6914d43f2a7ac177da2c2f6b52de3d7c88ac000000000100000001c33ebff2a709f13d9f9a7569ab16a32786af7d7e2de09265e41c61d078294ecf010000008a4730440220032d30df5ee6f57fa46cddb5eb8d0d9fe8de6b342d27942ae90a3231e0ba333e02203deee8060fdc70230a7f5b4ad7d7bc3e628cbe219a886b84269eaeb81e26b4fe014104ae31c31bf91278d99b8377a35bbce5b27d9fff15456839e919453fc7b3f721f0ba403ff96c9deeb680e5fd341c0fc3a7b90da4631ee39560639db462e9cb850fffffffff0240420f00000000001976a914b0dcbf97eabf4404e31d952477ce822dadbe7e1088acc060d211000000001976a9146b1281eec25ab4e1e0793ff4e08ab1abb3409cd988ac0000000001000000010b6072b386d4a773235237f64c1126ac3b240c84b917a3909ba1c43ded5f51f4000000008c493046022100bb1ad26df930a51cce110cf44f7a48c3c561fd977500b1ae5d6b6fd13d0b3f4a022100c5b42951acedff14abba2736fd574bdb465f3e6f8da12e2c5303954aca7f78f3014104a7135bfe824c97ecc01ec7d7e336185c81e2aa2c41ab175407c09484ce9694b44953fcb751206564a9c24dd094d42fdbfdd5aad3e063ce6af4cfaaea4ea14fbbffffffff0140420f00000000001976a91439aa3d569e06a1d7926dc4be1193c99bf2eb9ee088ac00000000

获取的数据是16进制的,其中前80个字节是区块头数据。对前80个字节数据进行双SHA256运算,得到当前区块的哈希值。前80字节数据为:

0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f337221b4d4c86041b0f2b5710

计算区块头哈希值的 Go 语言代码如下:

package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

const HashSize = 32

type Hash [32]byte

func (hash Hash) String() string {
	for i := 0; i < HashSize/2; i++ {
		hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i]
	}
	return hex.EncodeToString(hash[:])
}

func main() {
	block, _ := hex.DecodeString("0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f337221b4d4c86041b0f2b57100401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020602ffffffff0100f2052a010000004341041b0e8c2567c12536aa13357b79a073dc4444acb83c4ec7a0e2f99dd7457516c5817242da796924ca4e99947d087fedf9ce467cb9f7c6287078f801df276fdf84ac000000000100000001032e38e9c0a84c6046d687d10556dcacc41d275ec55fc00779ac88fdf357a187000000008c493046022100c352d3dd993a981beba4a63ad15c209275ca9470abfcd57da93b58e4eb5dce82022100840792bc1f456062819f15d33ee7055cf7b5ee1af1ebcc6028d9cdb1c3af7748014104f46db5e9d61a9dc27b8d64ad23e7383a4e6ca164593c2527c038c0857eb67ee8e825dca65046b82c9331586c82e0fd1f633f25f87c161bc6f8a630121df2b3d3ffffffff0200e32321000000001976a914c398efa9c392ba6013c5e04ee729755ef7f58b3288ac000fe208010000001976a914948c765a6914d43f2a7ac177da2c2f6b52de3d7c88ac000000000100000001c33ebff2a709f13d9f9a7569ab16a32786af7d7e2de09265e41c61d078294ecf010000008a4730440220032d30df5ee6f57fa46cddb5eb8d0d9fe8de6b342d27942ae90a3231e0ba333e02203deee8060fdc70230a7f5b4ad7d7bc3e628cbe219a886b84269eaeb81e26b4fe014104ae31c31bf91278d99b8377a35bbce5b27d9fff15456839e919453fc7b3f721f0ba403ff96c9deeb680e5fd341c0fc3a7b90da4631ee39560639db462e9cb850fffffffff0240420f00000000001976a914b0dcbf97eabf4404e31d952477ce822dadbe7e1088acc060d211000000001976a9146b1281eec25ab4e1e0793ff4e08ab1abb3409cd988ac0000000001000000010b6072b386d4a773235237f64c1126ac3b240c84b917a3909ba1c43ded5f51f4000000008c493046022100bb1ad26df930a51cce110cf44f7a48c3c561fd977500b1ae5d6b6fd13d0b3f4a022100c5b42951acedff14abba2736fd574bdb465f3e6f8da12e2c5303954aca7f78f3014104a7135bfe824c97ecc01ec7d7e336185c81e2aa2c41ab175407c09484ce9694b44953fcb751206564a9c24dd094d42fdbfdd5aad3e063ce6af4cfaaea4ea14fbbffffffff0140420f00000000001976a91439aa3d569e06a1d7926dc4be1193c99bf2eb9ee088ac00000000")
	first := sha256.Sum256(block[:80]) // 选择区块的前80个字节,即区块头数据,进行第一次哈希运算
	second := sha256.Sum256(first[:])  // 将第一次结果继续哈希,得到第二个结果,该结果为区块哈希值
	fmt.Printf("blockheader is: \n%x\n", block[:80])
	fmt.Printf("doublehash is (little-endian):\n%v\n", hex.EncodeToString(second[:]))
	fmt.Printf("blockheader hash is (big-endian):\n%v\n", Hash(second))
}

运行结果:

$ go run test.go
blockheader is:
0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f337221b4d4c86041b0f2b5710
doublehash is (little-endian):
06e533fd1ada86391f3f6c343204b0d278d4aaec1c0b20aa27ba030000000000
blockheader hash is (big-endian):
000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506

或者,使用 bx 工具进行区块头验证过程如下:

$ bx sha256 0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f337221b4d4c86041b0f2b5710
00844eeb8713eb62bc33df34ca0cfa7af2ee152a6b16788fd3f2fea69861f3c8
$ bx sha256 00844eeb8713eb62bc33df34ca0cfa7af2ee152a6b16788fd3f2fea69861f3c8
06e533fd1ada86391f3f6c343204b0d278d4aaec1c0b20aa27ba030000000000
$ bx bitcoin256 0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f337221b4d4c86041b0f2b5710
000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506

可以发现经过两次SHA256运算之后得到的结果就是第100000号区块哈希值。

4 挖矿过程为什么要计算两次哈希值?

从前面的描述中我们知道了比特币挖矿过程中,需要对80个字节的区块头数据进行Double_SHA256运算。那么为什么要两次哈希运算呢?比特币挖矿算法中使用的哈希算法是SHA2-256,该算法设计与SHA-1类似。2005年,王小云团队在一个密码学会议上正式宣布在 O(2^69) 时间复杂度内就能找到一组碰撞,该复杂度远低于 O(2^80) 的理论安全值。采用双哈希运算能在一定程度上加强SHA-1的安全性。而中本聪在设计比特币的挖矿算法的时候,可能也是基于相同的考虑,虽然在理论上并未出现对SHA2-256算法的攻击。为了减弱生日攻击的威胁,区块头数据要对SHA2-256算法运算两次。

5 参考资料

  • Hashcash:选择双哈希作为挖矿算法的原因分析。
  • SHA-1:介绍了SHA-1算法,并列举了SHA-1算法的破解经过。

你可能感兴趣的:(区块链,比特币)