天天看點

比特币挖礦算法

目錄

    • 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算法的破解經過。