從門羅幣的硬分叉看加密貨幣的算力大戰
本篇文章是為 AppWorks #17 的招募活動而寫的,另發表於 TO 等網路媒體及 AppWorks 部落格上。
門羅幣 (Monero) 是加密貨幣的後起之秀。在大眾的目光仍停留在比特幣、以太坊等主流加密貨幣時,許多需要真正匿名交易特性的使用者已經悄悄轉投門羅幣的懷抱。
隨著加密貨幣的盛行,市場上出現了越來越多專為挖礦而設計的礦機。為了對抗使用專用晶片 (ASIC) 挖礦的礦機,不讓這些礦機主導門羅幣的區塊鏈開採,門羅幣的開發者社群決定從第 1546000 個區塊開始,更改挖礦的演算法,讓晶片礦機無法投入門羅幣的開採。台北時間 4 月 6 日下午 4 點 22 分 11 秒,門羅幣區塊鏈的第 1546000 個區塊被挖出,從這裡開始,門羅幣將採用新的 CryptoNight V7 演算法來挖礦。
到底挖礦是怎麼回事,礦機又是如何運作的?為什麼門羅幣的開發者對與專用晶片的礦機這麼反感,乃至於要發起演算法變更這麼重大的動作,來對抗晶片礦機 ?
挖礦
加密貨幣的挖礦,指的是區塊鏈網路上的使用者透過某種相互協調的方法,選出一個使用者來,由這個使用者來整理、打包這一回合的交易資料。這個選出幸運兒的方法,稱為共識演算法 (consensus algorithm)。
受到比特幣的啟發,現今絕大多數的加密貨幣使用的共識演算法都是工作量證明 (Proof of Work, PoW) 演算法。PoW 演算法是這樣運作的:由大家各自解一個很難解但是很容易驗證的題目,看誰先解出來,就對全世界大喊 “我解出來了,答案在這裡!”,此時大家會拿這個答案去驗算。如果驗算的結果正確,大家就承認第一個解出來的這個人是這一輪的幸運兒,由他獲得這一輪打包資料的殊榮,同時也能領取這一回合的挖礦獎勵。
我們舉比特幣當範例來說明挖礦怎麼挖好了。
比特幣選擇了 SHA256 這個雜湊函數 (hash function),當作它的挖礦演算法核心。所謂 hash function,就是我們前面提過的那種難解開易驗算的題目之一。以 SHA256 來說,它的輸入是一個長度介於 0 到 (2^64-1) bits 的字串,而輸出則是一個 256bits 的數字。比如說我把 “Bird is handsome” 這個字串拿去做 SHA256 計算,就會得到這個數字:
SHA256("Bird is handsome") -> 0x8066544d3ee23a0acf4dc9f21b14276f24bbfc1bb1de87ebdf3508ac4dbda367
如果我不小心把 handsome 打錯一個字母,把其中的 o 打成了 a,算出來的 SHA256 就會變成:
SHA256("Bird is handsame") -> 0xb0327afc47f4fef6850236b2c854b3b3aff13d4514ec4417a48dda7d4d45c2d3
有沒有發現 ? 差之毫釐,失之千里。只不過改變了輸入中的一個字元,SHA256 算出來的結果就變得連他媽媽都不認得了。這就是 hash function 的特性:輸入中的任何變化,都會讓輸出有著完全無法預期的變化,而且你幾乎找不到任何關於 “輸入的變化” 與 “輸出的變化” 之間的規則,因此從 hash function 的輸出去反算輸入,理論上幾乎是不可能的。
我們現在可以用這個特性來出題了。假設我們在 “Bird is handsome” 這個字串後面加上一個 6 位數字:比如說 778899 好了,這時就會算出一個新的 SHA256 值:
SHA256("Bird is handsame 778899") -> 0xa4cdaaadc70539d23342806fcee58399d6c5f8afa8ced80b61e03d3da01a877c
然後我把題目定為:請大家依照以上的格式,找出一個數字,使得這個字串的 SHA256 輸出,最前面的 2 位數都是 0。
這要這麼解呢?依照 hash function 的特性,你完全無法從輸出猜測輸入,因此唯一的解法就是用試的,或是用猜的。我們就老老實實從 000000 開始試好了:
SHA256("Bird is handsome 000000") -> 0x60101f7d7c7a7e61ac2e4db4f7f45fea43c2e548bfec1af36d523e8b66c8d70d SHA256("Bird is handsome 000001") -> 0x32136485e25a23e0bba4c2474ee267e71d48b6696596c2d25a81b5b5d3c48900 SHA256("Bird is handsome 000002") -> 0xe7316c060d9510a995aa08f7c635786e866e284447046ddf393429569d065d9e ......
試到 000056 時,我們就找到了一組解:
SHA256("Bird is handsome 000056")
-> 0x00999635ad2ba3441af06f8738b7212a52060d6cd1e4b3feaf04ea70323c8e27
事實上這解不保證是唯一的,這是 hash function 的特性之一。如果有人從三千多開始往上試,也會找到另一組解:
SHA256("Bird is handsome 003280")
-> 0x00696cc645d0cd17f014aaac0273c004eceea1d534fdfe04f0d30f229c4cfe07
但如果題目變成 “使得它的輸出,最前面的 3 位數都是 0” 的話,難度就會上升許多,因為符合這個條件的數字一定比兩個 0 的要少很多。
因此,藉由調整使得解答成立的條件,我們可以控制題目的難度。
在比特幣中,那個字串來自前一個區塊的資訊,以及這個區塊需要打包的資料,而那個數字則是 header 中需要解算出來的一個欄位。
比特幣的每一個區塊都有一個 80 bytes 的 header,它的格式是這樣的:
Bytes | Name | Data Type | Description |
---|---|---|---|
4 | version | int32_t | 區塊的版本 |
32 | previous block header hash | char[32] | 用前一個區塊的 header 所計算出來的 SHA256 值。 這是確保每個區塊跟前一個區塊環環相扣的關鍵。 |
32 | merkle roothash | char[32] | 這個區塊中的所有交易,經過一個叫 Merkle Tree 的演算法所算出來的值。 這是確保一個區塊中的交易不會被竄改的關鍵。 |
4 | time | uint32_t | 這個區塊的時間 |
4 | nBits | uint32_t | 目前的挖礦難度,也就是要計算的 SHA256 值前面要有幾個 0 (事實上還要再複雜一些,為了說明方便我們暫時這樣解釋) |
4 | nonce | uint32_t | 礦工所追尋的聖杯,就是要猜的數字 |
前五個欄位都是已知的:版本就是目前使用的協定版本、從區塊鏈上拿到前一個區塊 header 的 SHA256 值、決定要打包哪些交易然後算出它們的 Merkle hash、時間看看電腦的時鐘就知道了、挖礦難度則有另外一個演算法根據前面一段時間挖礦的產出速度決定,當一個礦工把前面五個欄位準備好後,他就可以開始挖礦。
挖礦的方法就是:調整第六個欄位,也就是 nonce 的數字,使得整個 header 的 SHA256 值前面的 0 的數量,符合挖礦難度那一欄的值。
難度與算力
挖礦難度也是比特幣系統設計上一個很巧妙的地方。比特幣開始上線時,挖礦的難度是 8 個 0,也就是找出來的 nonce 必須讓這個 header 的 SHA256 前面有 8 個 0。我們可以去比特幣的區塊鏈上查一下歷史紀錄就知道。撈一下 2009 年的幾個區塊來看:
Height | Time | Hash | Size (kB) |
---|---|---|---|
2056 (Main Chain) | 2009-01-28 00:06:35 | 000000009737fc85becda1878da804a7dd7e393999a356afb9aa49ac612fd838 | 0.22 |
2057 (Main Chain) | 2009-01-28 00:16:29 | 00000000acd58d5632619a889f751c9f508f7869f05814e6ce4b004258969342 | 0.22 |
2058 (Main Chain) | 2009-01-28 00:38:00 | 00000000c5d078e6198193ec872d5400ce78257db3c4298f060344f86fe1fefa | 0.22 |
2059 (Main Chain) | 2009-01-28 01:00:57 | 000000004fdc363a128276c9acbf4e4e9217a9a88852b6682d71a637d029c21f | 0.22 |
2060 (Main Chain) | 2009-01-28 01:26:13 | 000000005a2d6394da35269c09425f59f4d76c5b486b2d6b617f30bbd4fb79dd | 0.22 |
2061 (Main Chain) | 2009-01-28 01:34:39 | 00000000cd2ae5e37f774026a30f6880fb5de6e73f0c6f27cb0e119e607e4b6d | 0.22 |
2062 (Main Chain) | 2009-01-28 01:37:12 | 00000000d34199387862c42561031f5b25cc35bb9895e53dd07e30508748ced8 | 0.21 |
2063 (Main Chain) | 2009-01-28 01:49:45 | 000000003024fcc0de00e9af302c8bf69b0c625eea0ac3502fb25adb935b652c | 0.22 |
可以看到當時算出來的 hash,前面只有 8 個 0 就算有效了。
但隨著成千上萬的礦工投入挖礦,整個網路的計算能力也大幅上升,為了維持差不多每 10 分鐘算出一個區塊的速度不變,比特幣網路就根據難度調整的演算法慢慢地把 0 的數量增加。
我們看看最近幾天挖出來的區塊:
Height | Time | Hash | Size (kB) |
---|---|---|---|
517875 (Main Chain) | 2018-04-12 15:51:26 | 00000000000000000004379ca3a93767d6cfa38a33ca09a8516d448eb3f8f7a9 | 1,190.21 |
517874 (Main Chain) | 2018-04-12 15:25:44 | 00000000000000000021fb788a670625f57fab83f9a8284c159e707019e91a62 | 1,075.39 |
517873 (Main Chain) | 2018-04-12 15:25:19 | 00000000000000000022add65811dd5649b0f03644ace2d55cc8091b0875e484 | 1,164.86 |
517872 (Main Chain) | 2018-04-12 15:21:29 | 0000000000000000002b18eebda5a5e6df90370aeb10fe8ec8bfa3e69f736fe8 | 1,060.64 |
517871 (Main Chain) | 2018-04-12 15:20:15 | 000000000000000000492368243c8601cf531afed6be41c3a9ebce999c3ed8ce | 1,007.61 |
有效 hash 值所需要的 0 的數量已經暴增到 18 個。
每多一個 0,找到有效 hash 值的機率就變為原來的 1/16,也就是挖礦難度變成 16 倍。從 2009 年到現在已經多了 10 個 0,也就是説現在的挖礦難度是當年的 16^10 = 1,099,511,627,776 倍,1 兆倍。
換句話說,現在比特幣網路上的總計算能力,也就是大家一起猜數字的速度,是當年的 1 兆倍。
Nonce 是一個 32bit 的值,也就是有 2^32 差不多有 42 億種可能。當挖礦難度低的時候,這 42 億個數字中多半可以找到符合的解,但是隨著挖礦難度增加,很有可能會遇到無法在這 42 億個數字裡找到有效解的狀況,這時候礦工就要稍微調整一下前面的幾個欄位,再重新尋找有效的 nonce。由於版本、前一個 block 的 hash 值、難度這幾個欄位都是固定的,只有時間和 Markle Root Hash 這兩個欄位可以更動。常見的做法是調整 Merkle Root Hash 這個欄位,細節還蠻複雜的,這裡先不解釋。
整個比特幣的挖礦過程中,最複雜的計算就是這個 SHA256 的計算。而挖礦這件事情,說穿了,就是大家比賽猜數字,把選定的數字丟到 SHA256 裡面去算,看看你是不是那個幸運兒。至於要怎麼猜,是一個一個照順序猜,還是跳著猜,還是亂猜,都無所謂,因為就機率模型來說,不管你怎麼選數字,結果都是一樣的。
計算 SHA256 的速度越快,在同樣的時間裡能猜的數字就越多,你就越有可能猜中。這就是 PoW 演算法的精神:藉由證明 (proof) 你所提供的工作量 (work),以換取你在這一回合獲取挖礦獎勵的機會。
隨著比特幣的價格上揚,挖礦開始變成一門不錯的生意。要挖得好、挖得快,關鍵就在於那個 SHA256 算得好不好、快不快。於是繼 CPU、GPU 被拿來挖礦之後,終於有人設計 IC 來挖礦了!
挖礦晶片
SHA256 演算法其實非常適合用 IC 來做,因為它的計算步驟都是單調、重複的布林運算和資料重排,用 Verilog 來寫的話其實才幾百行程式碼而已。於是就出現了用專用的 IC 來計算 SHA256 的挖礦機器,而且一舉把計算速度推升了好幾個數量級。
大部分的 GPU 計算比特幣 SHA256 的速度大概都在每秒 10 億次以下。但是,但是,重點來了,很多挖比特幣專用的 IC 都可以輕鬆達到每秒一兆次以上,而且耗電遠比 GPU 低得多。每秒一兆次是什麼概念呢 ? 就是一組 block header 的資料所搭配的 40 億組可能的 nonce 值,遇到這種挖礦的 IC,它不用 0.1 秒就能試完。要是題目出得不夠快,還餵不飽這種挖礦 IC 呢。
這種挖礦用的 IC 既便宜又省電,計算能力還比顯示卡高上許多,因此有越來越多的人利用這種裝置投入比特幣的開採。比特幣現在整個網路的計算能力大概是兩千多萬 Thash/sec (這數字大概是 2*10^19,用中文寫的話是 2000 “京”),如果都用 1Ghash/sec 的顯示卡來挖,要兩百億張這種顯卡才能挖到這個速度,地球上哪來這麼多顯示卡?當然都是用專用 IC,也就是 ASIC 在挖呀。
用 ASIC 礦機挖礦的現象,導致整個比特幣網路的計算能力集中在少數擁有大量礦機的團體手上,一般平民百姓不管是買不起礦機,還是不願為了挖比特幣而去買專門的礦機,都沒辦法自己在家裡用顯示卡參加挖掘比特幣的偉大行動,因為用顯示卡挖礦根本連 ASIC 礦機的車尾燈都看不到。
這個現象,讓比特幣的區塊鏈離 “去中心化” 的理念越來越遠。計算能力的過度集中,甚至導致中國的某個礦池一度掌握了超過全網一半以上的計算能力,而讓人擔憂比特幣網路會遭到所謂的 “51% 攻擊”。51% 攻擊指的是,如果有單一節點掌握了整個區塊鏈網路一半以上的計算能力,它就有辦法操縱、甚至改寫區塊鏈上的資料。(所以其實不用 51%,有 50.1% 或 50.01% 也可以,差別只在於攻擊成功的機率。)
其它的加密貨幣開發者注意到這個現象,紛紛開始想辦法透過演算法的設計,來避免這種 “ASIC 礦機之亂”。
以目前市值排名第二的加密貨幣以太坊 (Ethereum) 來說,它的挖礦演算法不像比特幣只需要計算 SHA256 這麼簡單。挖掘以太坊的計算過程需要參照一個叫做 DAG 的表格,這個表格每 30,000 個區塊要重新產生一次,而且它的大小會隨著區塊鏈的成長而增加。目前 Ethereum 的 DAG 表格大小大概是 2.4GB。
由於以太坊挖礦的計算過程需要隨時參照這張表格,如果要挖得快挖得好,這張表格就得放在速度很快的記憶體中,以便計算核心可以快速讀取。但以目前的 ASIC 技術來說,就算用 embedded DRAM 製程也沒辦法在同一個晶片上做出這個大小的記憶體。而外掛記憶體或 stacked-DRAM 則需要處理匯流排、記憶體介面等複雜的設計,讓個整體的設計變得相當複雜,成本也會大幅增加。它不是做不到,而是沒辦法像比特幣的挖礦晶片那樣用划算的方法做到。總之,以太坊的的演算法有很多巧思都是為了對抗 ASIC 挖礦而設計的。
因此,以太坊自 2015 年 7 月上線以來,雖然計算能力快速成長,始終沒有出現可以挖掘乙太幣的 ASIC 礦機。直到今年四月。
2018 年 4 月,中國的礦機大廠比特大陸 (Bitmain) 推出了一台叫做 Antminer E3 的 ASIC 礦機,售價八百美金,號稱能用 180Mhash/sec 的速度挖掘以太幣,而且只有 800W 左右的耗電。
雖然速度不像比特幣的 ASIC 礦機那麼快,但耗電和價格還是遠比使用顯示卡挖掘以太幣要低得多。這機器預計七月開始出貨,屆時對以太坊區塊鏈的算力會有多少衝擊,還有待觀察。唯一正面的影響大概是顯示卡缺貨的問題可以得到紓解。現在在礦工們瘋狂掃貨下,顯示卡真的很難買呀。
門羅幣保衛戰
至於前面提到的門羅幣,在開發之初除了考慮到要對抗 ASIC 礦機之外,也用演算法拉近了 GPU 和 CPU 的挖礦能力。門羅幣使用的挖礦演算法叫 CryptoNight,這個演算法的開發社群開宗明義就說了,這是個 “Egalitarian” (平等主義) PoW 演算法。他們希望在這個演算法之下,除了不會存在 ASIC 挖礦這種明顯中心化的行為之外,你也不用去買貴森森的高階顯卡來挖礦,因為門羅幣讓顯卡挖礦的速度跟 CPU 挖礦的速度差不多。
CryptoNight 演算法用了幾個方式實踐這個 “挖礦之前,人人平等” 的精神:
- 每個挖礦的單元需要 2Mb 的記憶體。它不像以太幣挖礦需要的記憶體那麼大,但 2Mb 對 ASIC 來說也是個不低的門檻。而這個大小,差不多可以符合現代 CPU 的 L3 快取記憶體大小 (以平均分配給每個核心的快取記憶體大小來計算)。
- 相對於 CPU 挖礦,GPU 挖礦的優勢在於它的平行處理架構:它可以同時執行成千上萬的挖礦工作。但由於每一個工作都需要各自的 2Mb 記憶體,而且 CryptoNight 演算法存取這塊記憶體的行為非常隨機,因此顯示卡上的 GDDR 記憶體不見得能負荷這樣大的記憶體存取頻寬。GDDR 記憶體的優勢在於循序讀寫的頻寬,在隨機存取上反而沒有優勢。
- CryptoNight 演算法的核心用到一種稱為 AES 的加密演算法。現在的 x86/x64 架構處理器都有專用的硬體電路和指令集來加速 AES 計算,這方面顯示卡的 GPU 完全沒有任何優勢。
但賠錢的生意沒人做,殺頭的生意有人做。隨著門羅幣的價格上漲,它的挖礦利潤也越來越高,終於還是出現了可以挖門羅幣的 ASIC 礦機。
一樣是比特大陸 (Bitmain) 幹的。
比特大陸在今年三月發表了一款叫做 Antminer X3 的礦機,宣稱在 550W 的耗電下,能以 220Khash/sec 的速度挖掘門羅幣。
相較於 AMD Radeon Vega 64 這種一張近兩萬塊的高階顯卡僅能用 2Khash/sec 左右的速度開採門羅幣,Antminer X3 似乎開啟了門羅幣 ASIC 採礦的新時代。
但門羅幣陣營也不是省油的燈。Antminer X3 的消息一出來,門羅幣的主要開發者 Riccardo Spagni 就在推特上開嗆了:
他公開宣示,“Antminer 這種 ASIC 礦機對門羅幣不管用“。
為了要達到這個目標,門羅幣社群決定進行一次 “硬分岔“ (hard fork),也就是更改門羅幣的挖礦演算法,讓 ASIC 礦機難以在新的 CryptoNight V7 演算法上運作。
如同我們在文章一開始說的,門羅幣在第 1546000 個區塊進行了硬分岔,修改了挖礦演算法。究竟這個變更有沒有成功讓門羅幣網路上的 ASIC 礦機現形呢 ? 我們來看看門羅幣的算力變化就知道了。
就在門羅幣執行硬分岔後,整個門羅幣網路的算力暴跌了三分之二以上,而且至今都還沒有爬回硬分岔之前的算力水準。
這中間的算力差距,大概就是被硬分岔踢出去的 Antminer X3 礦機所擁有的算力。Antminer X3 仍未開始交貨,因此這些算力應該是比特大陸內部在測試機器時所貢獻的算力。
至此,門羅幣社群算是成功地防堵了 Antminer X3 礦機把持門羅幣網路的算力。但目前仍不知道 Antminer X3 的挖礦晶片有多少彈性,會不會只需要修改軟體就能重新上線挖掘新演算法的門羅幣,還是它們就此跟門羅幣道別,改去挖其它仍使用舊版 CryptoNight 演算法的加密貨幣。
區塊鏈網路的中心精神是 “去中心化“,但由於挖礦帶來的豐厚利潤,各種礦池、礦場的規模化在所難免,也因此導致各加密貨幣網路的算力往集中化的趨勢傾斜。如同我們前面解釋過的,PoW 演算法藉由證明工作量來換取打包資料的權利以及領取挖礦獎勵,因此只要 PoW 演算法存在一天,這場開發者與礦機大戶的算力大戰就不會終止。
近期留言