比特幣挖礦的算法,可以簡單地總結為對區塊頭做兩次sha256哈希運算,得到的結果如果小于區塊頭中規定的難度目標,即挖礦成功。
區塊頭的結構 如下
那么挖礦的算法可以表達為
block_header = version + previous_block_hash + merkle_root + time + target_bits + nonce
for i in range (0 , 2 **32 ):
if sha256(sha256(block_header)) < target_bits:
break
else:
continue
簡單回顧下挖礦的流程。
挖礦節點首先對交易做驗證,剔除有問題的,然后通過一套自定義的標準來選擇哪些交易希望打包進區塊,比如通過交易費與交易占用的字節大小的比值超過某個門檻來判斷,這樣的交易才被認為有利可圖。當然,節點也可以特意選擇要加入某條交易,或者故意忽略某些交易,每個挖礦節點有很大的自由裁度權力。
如果是通過礦池挖礦的話,礦池的服務器會去篩選交易,然后分配給每個參與的礦機一個獨立的任務。這個任務的難度小于總的挖礦難度,完成了小難度的計算,即確認了自己參與的工作量。每臺不同的礦機計算的問題不會重復,當其中一臺礦機成功挖礦時,其他礦機依據工作量分配獲得的總收益。
一旦篩選好交易數據,按照時間排序,兩兩哈希,層層約減,通過這些交易就可以計算出一棵Merkle樹,可以確定一個唯一的摘要,這就是Merkl樹的根。
merkle樹中,任何節點的變化,都會導致merkle root發生變化,通過這個值,可以用來驗證區塊中的交易數據是否被改動過。
然后我們再依次獲取挖礦需要的每一項區塊頭的信息。 區塊頭只有80個字節,挖礦只需要對區塊頭進行運算即可。交易數據都通過merkle樹固定了下來,不需要再包含進來。而所謂的區塊鏈,其實也是通過區塊頭而鏈接在一起的 。下面的示意圖比較簡單明確地解釋了區塊鏈和區塊的構成。
比特幣區塊鏈示意圖
區塊頭中的信息,在挖礦前大部分已經是固定下來的,或者是可計算的。
版本號
跟隨比特幣客戶端而定,一段時間內不會改變 。即使要改變,也會有比特幣的核心開發人員來協調升級策略,這個可以理解為一個靜態常數。
前一區塊的哈希摘要
一次哈希即可 。前一區塊已經是打包好的。
默克爾樹的根
剛才已經得到了結果,根據本次交易包含的交易列表得到 。
時間
取打包時的時間 。也不需要很精確,前后幾秒,幾十秒也都可以。
難度目標
參考上兩周產生的區塊的平均生成時間而定 。兩周內如果平均10分鐘產生一個區塊的話,兩周會產生2016個區塊,軟件會計算最新的2016個區塊生成的時間,然后做對比,隨之調整難度,使得接下來產生的區塊的預期時間保持在10分鐘左右。因為最近的2016個區塊已經確定,所以這個數字也是確定的。
隨機數nonce
這個就是挖礦的目標 了。這是一個32位的數字。
隨機數可以變化,而且要從0試到最大值2^32。直到最后出現的hash結果,其數字低于難度目標值。不過以現在的計算機算力,一臺礦機用不了一秒就把全部的變化可能計算完了,所以還需要改變區塊內部的創幣交易中的附帶消息,這樣就讓merkle root也發生了變化,從而有更多的可能去找到符合要求的nonce。
合格的區塊條件如下:
SHA256D(Blockherder) < F(nBits)
其中,SHA256D(Blockherder)就是挖礦結果,F(nBits)是難度對應的目標值 ,兩者都是256位,都當成大整數處理,直接對比大小以判斷是否符合難度要求。
為了節約區塊鏈存儲空間,將256位的目標值通過一定變換無損壓縮保存在32位的nBits字段里。具體變換方法為拆分利用nBits的4個字節,第1個字節代表右移的位數,用V1表示,后3個字節記錄值,用V3表示,則有:
F(nBits)=V_3 * 2^(8*(V_1-3) )
此外難度有最低限制,也就是說 F(nBits) 有個最大值,比特幣最低難度取值nBits=0x1d00ffff,對應的最大目標值為:
0x00000000FFFF0000000000000000000000000000000000000000000000000000
因此挖礦可以形象的類比拋硬幣 ,好比有256枚硬幣,給定編號1,2,3……256,每進行一次Hash運算,就像拋一次硬幣,256枚硬幣同時拋出,落地后要求編號前n的所有硬幣全部正面向上。
挖礦中,第一筆交易是創幣交易。創幣交易可以附帶一段文字消息,這段信息可以用來提供更多的nonce. 比如中本聰在挖出創世區塊時植入的信息。
The Times 03/Jan/2009 Chancellor on brink of second bailout for banks