1. Ethash 演算法
1.1 Ethash
Ethash是以太坊1.0中使用的PoW(工作量證明)演算法,它是Hashimoto演算法結合Dagger之後產生的一個變種。它的特點是計算的效率基本與CPU無關,卻和記憶體大小和記憶體頻寬正相關。因此通過共享記憶體的方式大規模部署的礦機晶片並不能在挖礦效率上有線性或者超線性的增長。
該演算法的一般流程如下:
首先根據塊資訊計算一個種子(seed, c++程式碼中為seedhash)
使用這個種子,計算出一個16MB的cache資料。輕客戶端需要儲存這份cache.
通過cache,計算出一個1GB(初始大小)的資料集(DAG),DAG可以理解為是一個完整的搜尋空間,全客戶端和礦工需要儲存完整的DAG,挖礦過程中需要從DAG中重複的隨機抽取資料拿去和其他資料計算mixhash,DAG中每個元素的生成只依賴於cache中的少量資料。每到一個新的紀元DAG會完全不一樣,並且它的大小也隨時間線性增長。
由於僅根據cache就可以使用少量記憶體快速的計算出DAG中指定位置的資料,所以輕客戶端只需要儲存cache就可以高效的進行校驗。
1.2 記憶體難解
由於比特幣將hash演算法作為pow工作量證明的重要手段,後續的各種採用pow的數字貨幣也延續了這個設計,以SHA256、MD5(MD5後來被證明不具備強碰撞性數字貨幣一般不用)為代表演算法。在設計之初都是算力敏感型,意味著計算資源是瓶頸,主頻越高的 CPU 進行 Hash 的速度也越快。這個設計直接導致後來的礦機出現,採用ASIC晶片的礦機更是將這種運算能力成倍提升,更多礦場的出現使得當時的比特幣面臨算力中心化的威脅。為了限制計算能力的依賴,人們開始尋求新的演算法,既然要限制CPU的能力,目光自然投向儲存依賴,也就是記憶體依賴。
Hashimoto演算法採用IO飽和的策略來對抗ASIC,使記憶體讀取成為採礦過程中的限制因素。
Dagger演算法使用DAG(directed acyclic graphs 有向無環圖)來同時實現記憶體難解和記憶體易驗證兩個特點。 主要原理是,計算每個nonce需要DAG中的一小部分,採礦過程需要儲存完整的DAG,禁止每次計算DAG的相應子集,而驗證過程是允許的。
1.3 引數定義
WORD_BYTES | 4 | Word的位元組數 |
---|---|---|
DATASET_BYTES_INIT | 2**30 1GB | Dataset的初始大小 | |
DATASET_BYTES_GROWTH | 2**23 8MB | 每個紀元dataset的增長量 | |
CACHE_BYTES_INIT | 2**24 16MB | Cache的初始大小 | |
CHCHE_BYTES_GROWTH | 2**17 128KB | 每個紀元cache的增長量 | |
CACHE_MULTIPLIER | 1024 | Size of the DAG relative to the cache | |
EPOCH_LENGTH | 30000 | 每個epoch的塊數 | |
MIX_BYTES | 128 | Mix的寬度 | |
HASH_BYTES | 64 | Hash的長度 | |
DATASET_PARENTS | 256 | 每個資料集元素的parents數量 | |
CACHE_ROUNDS | 3 | 計算cache時的輪數 | |
ACCESSES | 64 | Hashimoto迴圈的次數 | |
2 DAG
DAG是ethash演算法中需要頻繁訪問的資料集,這個為每個epoch生成的。DAG要花很長時間生成,如果客戶端至少按照需要生成它,那麼在找到新epoch第一個區塊之前,每個epoch過渡都要等待很長時間。然而,DAG的生成只取決於區塊數量,所以可以預先計算出DAG來避免在每個epoch過渡過長的等待時間。
DAG的生成流程如下:
2.1 Dag_size 和Cache_size
每個epoch的dagsize和cachesize都不同,上面已經定義了創世時的初始值,以太坊還提供了一個表來儲存接下來2048個紀元(大約20年)的各個值。詳見官網或原始碼cpp-ethereum/libethash/data_sizes.h.
獲取datasize 和cachesize的方法如下:
2.2 Seedhash
演算法中需要一個seedhash,由下面程式生成,從程式可見每個epoch的seed是不變的。
2.3 Cache
使用seedhash計算cache。
2.4 DAG
最後使用cache計算DAG,light引數中儲存的是cache資料.
2.5 DAG檔案
DAG每次生成都需要很長時間,因此生成時候需要存在檔案中,再使用mmap對映到記憶體中。DAG檔案路徑一般如下
Mac/Linux : $HOME/.ethash/full-R–
Windows: $HOME/Appdata/Local/Ethash/full-R–
是ethash演算法的版本號,在libethash/ethash.h 中REVISION定義。
是上面計算出來的seedhash
路徑下可能會有多個DAG檔案,這取決於使用者或者客戶端是否刪除過時的DAG檔案。
格式:
DAG檔案以8位元組的幻數開頭,值為0xfee1deadbaddcafe, 以小端格式寫入。接下來是小端格式寫入的dataset資料。
3 Ethash實現
3.1 Ethash
圖1 演算法流程圖
引數說明:
Header_hash: 是當前塊頭部資料的hash值,在礦機呼叫get_ethwork時從任務引數中獲取。
Nonce: 是每次計算ethash使用不同的數,不能重複。可以取時間戳或隨機數作為起始值,然後遞增。
對於礦工來說,如果result的值小於或等於target,那麼就完成了挖礦過程,將當前的nonce和mix_hash作為工作量證明提交工作;如果result的值大於target,那麼就需要改變nonce的值,再次呼叫ethash演算法.
Ethash演算法程式如下:
從圖中看,每次ethash從DAG隨機取64
1024*
1024/8192=33554432次ethash運算,即33MH/s的算力。可見,該演算法對記憶體頻寬的要求很高。
3.2 快速驗證
當驗證一個工作提交是否有效時,速度很快。
下面是快速驗證程式:
感謝HPB團隊整理。
稿源:巴位元資訊(www.8btc.com/article/244…)