導語:本課堂用通俗易懂的系列內容爲大家呈現區塊鏈與密碼學領域相關知識。這裏有知識也有故事,從感興趣到有樂趣,全民課堂等你來學。

【本課堂內容全部選編自PlatON首席密碼學家、武漢大學國家網絡安全學院教授、博士生導師何德彪教授的《區塊鏈與密碼學》授課講義及互聯網,版權歸屬其原作者所有,如有侵權請立即與我們聯繫,我們將及時處理。】

比特幣的挖礦

如何優雅地獲得你人生中第一枚比特幣?這是一個值得思考的問題。我們在第二堂課的時候簡單講過挖礦對於比特幣價格和走勢的影響,以及挖礦周邊衍生出的各行各業。今天我們詳細說說挖礦原理。

“挖礦”成功即是該節點成功獲得當前區塊記賬權,也就是說其他節點就“照抄”該挖礦成功的節點的當前區塊。獲得記賬權的節點會獲取一定數量的比特幣獎勵,以此激勵比特幣網絡中的所有積極參與記賬工作。該獎勵包含系統獎勵和交易手續費兩部分,系統獎勵則作爲比特幣發行的手段。

最初每生產一個“交易記錄區塊”可以獲得50比特幣的系統獎勵,爲控制比特幣發行數量,該獎勵每4年就會減半,到2140年會基本發放完畢,最終整個系統中最多的只能有2100萬個比特幣。如果按照目前比特幣的價格計算,比特幣的總價值將在2000億美金左右。

注:價格取自2020年5月30日13:35

比特幣系統大約每10分鐘會記錄一個數據塊,這個數據塊裏包含了這10分鐘內全網待確認的部分或全部交易。所謂的“挖礦”,就是爭奪將這些交易打包成“交易記錄區塊”的權利。比特幣系統會隨機生成一道數學難題,後續會詳細描述該數學難題,所有參與挖礦的節點一起參與計算這道數學難題。首先算出結果的節點獎獲得記賬權。

每個節點會將過去一段時間內發生的、尚未經過網絡公認的交易信息進行收集、檢驗、確認,最後打包並加簽名爲一個無法被篡改的“交易記錄區塊”,並在獲得記賬權後將該區塊進行廣播,從而讓這個區塊被全部節點認可,讓區塊中的交易成爲比特幣網絡上公認已經完成的交易記錄,永久保存。

挖礦最主要的工作就是計算上文提到的數學難題,最先求出解的礦工即可獲得該塊的記賬權。在介紹這個數學難題前,先簡單介紹一下哈希算法。哈希算法的基本功能概括來說,就是把任意長度的輸入值通過一定的計算,生成一個固定長度的字符串,輸出的字符串即爲該輸入的哈希值。比特幣系統中採用SHA-256算法,該算法最終輸出的哈希值長度爲256bit。

1.5.1

挖礦的原理

講到挖礦原理,首先我們要了解一種“植物”——Merkle樹

比特幣科普之Merkle樹是什麼樹?

Merkle樹通常稱爲Merkle Hash Tree,是數據結構中所說的樹,常用於高效彙總和驗證大數據集的完整性.具有以下特點:

① 默克爾樹常見的結構是二叉樹,但它也可以是多叉樹,它具有樹結構的全部特點。

② 默克爾樹的基礎數據不是固定的,因爲它只要數據經過哈希運算得到的hash值。

③ 默克爾樹是從下往上逐層計算的,就是說每個中間節點是根據相鄰的兩個葉子節點組合計算得出的,而根節點是根據兩個中間節點組合計算得出的,所以葉子節點是基礎。

比特幣中每個區塊生成時,需要把上一個區塊的哈希值、本區塊的交易信息的默克爾樹根、一個未知的隨機數(nonce)拼在一起計算一個新的哈希值。爲了保證10分鐘產生一個區塊,該工作必須具有一定難度,即哈希值必須以若干個0開頭。哈希算法中,輸入信息的任何微小改動即可引起哈希值的巨大變動,且這個變動不具有規律性。因爲哈希值的位數是有限的,通過不斷嘗試隨機數nonce,總可以計算出一個符合要求的哈希值,且該隨機數無法通過尋找規律計算出來。這意味着,該隨機數只能通過暴力枚舉的方式獲得。挖礦中計算數學難題即爲尋找該隨機數的過程。

哈希值由16進制數字表示,即每一位有16種可能。根據哈希算法的特性,出現任何一個數字的概率是均等的,即每一位爲“0”的概率爲1/16.要求某一位爲“0”平均需要16次哈希運算,要求前n位爲“0”,則需要進行哈希計算的平均次數爲16的n次方。礦工爲了計算出該隨機數,需要花費一定的時間進行大量的哈希運算。某個礦工成功計算出該隨機數後,則會進行區塊打包並全網廣播。

其他節點收到廣播後,只需對包含隨機數的區塊按照同樣的方法進行一次哈希運算即可,若哈希值以“0”開頭的個數滿足要求,且通過其他合法性校驗,則接受這個區塊,停止本地對當前區塊隨機數的尋找,開始下個區塊隨機數的計算。

隨着技術的發展,進行一次哈希計算速度越來越快,同時隨着礦工的逐漸增多,算出滿足哈希值以一定數量“0”開頭的隨機數的時間越來越短。爲保證比特幣始終按照平均每10分鐘一個區塊的速度出塊,必須不斷調整計算出隨機哈希計算的平均次數,即調整哈希值以“0”開頭的數量要求,以此調整難度。比特幣中,每生成2016個區塊就會調整一次難度,即調整週期大約爲兩週(2016x10min=14天)。也就是說,對比生成最新2016個區塊花費的實際時間和按照每10分鐘出一個塊生成2016個塊的期望時間,若實際時間大於期望時間則降低難度,若實際時間小於期望時間則增加難度。

同時,爲防止難度變化波動太大,每個週期調整幅度必須小於一個因子(當前爲4倍)。若幅度大於4倍,則按照4倍調整。由於按照該幅度調整,出塊速度仍然不滿足預期,因此會在下一個週期繼續調整。

1.5.2

礦池的原理

隨着區塊鏈的日漸火爆,參與挖礦的人越來越多,按照比特幣原本的設計模式,只有成功打包一個區塊的人才能獲取獎勵。如果每個礦工都獨立挖礦,在如此龐大的基數下,挖礦成功的概率幾乎爲0,只有一個幸運兒可以獲取一大筆財富,其他礦工投入的算力、電力資源就會白白虧損。或許投入一臺礦機,持續挖礦好幾年甚至更久才能挖到一個區塊。

爲了降低這種不確定性,礦池應運而生。假如有10萬礦工參與挖礦工作,這10萬礦工的算力和佔這個網絡的10%,則這10萬個礦工中的某個礦工成功挖到下個塊的概率即爲1/10。即平均每個礦工成功挖到下個區塊的概率爲1/1000000,即平均每個礦工要花費19年可以成功挖到一個區塊,然後獲得相應的比特幣獎勵。這種挖礦模式風險過大,幾乎沒人可以承受。但是假設這10萬個礦工共同協作參與挖礦,則平均每100分鐘即可成功挖到一個區塊,然後按照每個礦工提供的算力分配該次收益。這10萬個礦工的收益也會趨於穩定。

協調礦工進行計算的思路也非常簡單,礦池將打包區塊需要的交易等信息驗證完成後發送給礦工,然後降低礦工的挖礦難度。比如某個時段比特幣系統需要哈希值“0”開頭的個數大於50個,礦池可以將難度降低到40個“0”開頭,礦工找到一個40個“0”開頭哈希值的方案後,即可提交給礦池。礦池收到一個滿足哈希值“0”開頭個數大於50個的方案時,即可提交至比特幣網絡。

當然,你也許會想:如果礦工計算得到一個“0”開頭個數大於50的哈希值後,則直接提交給比特幣網絡,獨享該區塊的收益;如果計算得到一個“0”開頭數在40到50之間的則提交到礦池,享受整個礦池分配的收益。該方案當然是行不通的,因爲區塊內容是由礦池發送給礦工的,即受益者地址已經包含在該區塊中了,即使直接提交,最終受益的也是礦池。如果修改該地址,即意味着區塊內容改變,則前面計算的哈希值也無效了。最後礦池按照礦工提交方案數量計算貢獻的算力,最後根據算力分配收益。

當前的主流挖礦協議是stratum,以前還有GBT (getblocktemplate )、getwork等幾種協議,它們都過時了。可以用免費的Cpuminer軟件把協議調通。

軟件地址爲:

https://sourceforge.net/projects/cpuminer/files

接下來我們看看挖礦的具體流程吧~

客戶端首先向服務器發送subscribe指令

{"id": 1, "method": "mining.subscribe", "params": ["cpuminer/2.5.0"]}

參數中指名礦工軟件的名稱和版本號。

服務器端返回信息

比特幣挖礦實際上就是去尋找隨機數nonce,有時所有的隨機數都試遍了,仍無法滿足目標,就需要用到extra nonce。

客戶端發送認證信息

用戶名是錢包的地址,我這裏使用的是比特幣測試網絡的地址,並沒有以1開頭。

服務器返回true,表示用戶驗證通過

{"id":2,"result":true,"error":null}

服務器端發回難度設置消息

{"id":null,"method":"mining.set_difficulty","params":[8]}

服務器發送通知消息

礦工可能用到了多線程,所以需要job id來區分不用的線程,後面是一堆用於區塊生成的信息。

客戶端發現一個nonce,提交給服務器

服務器返回結果

返回true表示服務器認可客戶端的工作。

相關文章