前言
Ethash實現(xiàn)了PoW嚎京,PoW的精妙在于通過一個隨機數(shù)確定啃擦,礦工確實做了大量的工作起意,并且是沒有辦法作弊的。接下來將介紹:
- Ethash的挖礦本質变屁。
- Ethash是如何挖礦的眼俊。
- 如何驗證Ethash的隨機數(shù)。
Ethash的挖礦本質
挖礦的本質是找到一個隨機數(shù)粟关,證明自己做了很多工作(計算)疮胖。在Ethash中,該隨機數(shù)稱為Nonce
闷板,它需要滿足一個公式:
Rand(hash, nonce) ≤ MaxValue / Difficulty
其中澎灸,
- hash:去除區(qū)塊頭中Nonce、MixDigest生成的哈希值遮晚,見
HashNoNonce()
性昭。 - nonce:待尋找的符合條件的隨機數(shù)。
- MaxValue:固定值2^256县遣,生成的哈希值的最大取值糜颠。
- Difficulty:挖礦難度汹族。
- Rand():使用hash和nonce生成一個哈希值,這其中包含了很多哈希運算其兴。
以上參數(shù)中顶瞒,在得到區(qū)塊頭的hash之后,只有nonce是未知的元旬。
公式的含義是榴徐,使用hash和nonce生成的哈希值必須落在合法的區(qū)間。利用下圖介紹一下匀归,Rand()函數(shù)結果取值范圍是[0, MaxValue]坑资,但只有計算出的哈希值在[0, MaxValue / Difficulty]內,才是符合條件的哈希值穆端,進而該Nonce才是符合條件的袱贮,否則只能再去尋找下一個Nonce。
以太坊可以通過調整Difficulty來調節(jié)當前挖礦的難度徙赢,Difficulty越大字柠,挖礦的難度越大探越。當Difficulty越大時狡赐, MaxValue / Difficulty
越小,合法的哈希值范圍越小钦幔,造成挖礦難度增加枕屉。
哈希值滿足條件的概率是 p = (MaxValue / Difficulty) / MaxValue = 1 / Difficulty
,礦工需要進行1 / p = Difficulty
次的判斷鲤氢,才有可能找到一個符合條件的Nonce搀擂,當前以太坊難度為3241847139727150。
如何挖礦
Ethash挖礦的主要思想是卷玉,開啟多個線程去尋找符合條件的Nonce哨颂,給每個線程分配一個隨機數(shù),作為本線程的Nonce的初始值相种,然后每個線程判斷當前的Nonce是否符合上面的公式威恼,如果不符合,則把Nonce加1寝并,再次進行判斷箫措,這樣不定的迭代下去,直到找到一個符合條件的Nonce衬潦,或者挖礦被叫停斤蔓。
接下來介紹挖礦的幾個主要函數(shù)的實現(xiàn),它們是:
- 挖礦的入口Seal函數(shù)镀岛。
- 挖礦函數(shù)mine函數(shù)弦牡。
- 挖礦需要的數(shù)據(jù)cache和dataset友驮。
- Rand()函數(shù)的實現(xiàn)hashimotoFull和hashimoto。
挖礦入口Seal()
Seal
是引擎的挖礦入口函數(shù)喇伯,它是管理崗位喊儡,負責管理挖礦的線程。它發(fā)起多個線程執(zhí)行Ethash.mine
進行并行挖礦稻据,當要更新或者停止的時候艾猜,重新啟動或停止這些線程。
挖礦函數(shù)mine()
mine
函數(shù)負責挖礦捻悯。Seal
在啟動每一個mine
的時候匆赃,給它分配了一個seed
,mine
會把它作為Nonce
的初始值今缚,然后生成本高度使用的dataset
算柳,然后把dataset, hash, nonce
傳遞給hashimotoFull
函數(shù),這個函數(shù)可以認為是原理介紹中的Rand
隨機函數(shù)姓言,他會生成哈希值Result
瞬项,當Result <= Target
的時候,說明哈希值落在符合條件的區(qū)間了何荚,mine找到了符合條件的Nonce囱淋,使用Digest和nonce組成新的區(qū)塊后,發(fā)送給Seal
餐塘,否則驗證下一個Nonce是否是符合條件的妥衣。
挖礦需要的數(shù)據(jù)cache和dataset
dataset
用來生成Result
,而cache
用來生成dataset
戒傻。至于如何使用dataset
生成Result
在hashimoto()
中講述税手,本節(jié)介紹如何生成dataset。
dataset和cache中存放的都是偽隨機數(shù)需纳,每個epoch的區(qū)塊使用相同的cache和dataset芦倒,并且dataset需要暫用大量的內存。剛開始時cache是16MB不翩,dataset是1GB兵扬,但每個epoch它們就會增大一次,它們的大小分別定義在datasetSizes
和cacheSizes
慌盯,dataset每次增長8MB周霉,最大能達到16GB,所以挖礦的節(jié)點必須有足夠大的內存亚皂。
使用cache生成dataset俱箱。使用cache的部分數(shù)據(jù),進行哈希和異或運算灭必,就能生成一組dataset的item狞谱,比如下圖中的cache中黃色塊乃摹,能生成dataset中的黃色塊,最后把這些Item拼起來就生成了完整的Dataset跟衅,完成該功能的函數(shù)是generateDataset
孵睬。
dataset.generate()
是dataset的生成函數(shù),該函數(shù)只執(zhí)行一次伶跷,先使用generateCache()
生成cache掰读,再將cache作為generateDataset()
的入?yún)⑸蒬ataset,其中需要重點關注的是generateDatasetItem()
叭莫,該函數(shù)是根據(jù)部分cache蹈集,生成一組dataset item,驗證PoW的nonce的時候雇初,也需要使用該函數(shù)拢肆。
Rand()的實現(xiàn)hashimotoFull()和hashimoto()
hashimotoFull
功能是使用dataset、hash和nonce生成Digest和Result靖诗。它創(chuàng)建一個獲取dataset部分數(shù)據(jù)的lookup函數(shù)郭怪,該函數(shù)能夠返回連續(xù)的64字節(jié)dataset中的數(shù)據(jù),然后把lookup函數(shù)刊橘、hash和nonce傳遞給hashimoto
米辐。
hashimoto
的功能是根據(jù)hash和nonce琼富,以及l(fā)ookup函數(shù)生成Digest
和Result
侠坎,lookup函數(shù)能夠返回64字節(jié)的數(shù)據(jù)就行杀餐。它把hash和nonce合成種子据途,然后根據(jù)種子生成混合的數(shù)據(jù)mix绞愚,然后進入一個循環(huán),使用mix和seed獲得dataset的行號颖医,使用lookup獲取指定行的數(shù)據(jù)位衩,然后把數(shù)據(jù)混合到mix中,混合的方式是使用哈希和異或運算熔萧,循環(huán)結束后再使用哈希和異或函數(shù)把mix壓縮為64字節(jié)糖驴,把mix轉為小端模式就得到了Digest,把seed和mix進行hash運算得到Result佛致。
如何驗證
PoW的驗證是證明出塊人確實進行了大量的哈希計算贮缕。Ethash驗證區(qū)塊頭中的Nonce
和MixDigest
是否合法,如果驗證通過俺榆,則認為出塊人確實進行了大量的哈希運算感昼。驗證方式是確定區(qū)塊頭中的Nonce
是否符合公式,并且區(qū)塊頭中的MixDigest
是否與使用此Nonce
計算出的是否相同罐脊。
驗證與挖礦相比定嗓,簡直是毫不費力蜕琴,因為:
- 時間節(jié)省。驗證只進行1次
hashimoto
運算宵溅,而挖礦進行大約Difficulty次凌简。 - 空間節(jié)省。驗證只需要cache恃逻,不需要dataset雏搂,也就不需要計算龐大的dataset,因此不挖礦的驗證節(jié)點寇损,不需要很高的配置畔派。
接下來介紹驗證函數(shù)VerifySeal()
,以及根據(jù)cache生成Digest
和Result
的hashimotoLight()
润绵。
驗證函數(shù)VerifySeal
Ethash.VerifySeal
實現(xiàn)PoW驗證功能线椰。首先先判斷區(qū)塊中的Difficulty是否匹配,然后生成(獲瘸九巍)當前區(qū)塊高度的cache憨愉,把cache和nonce傳遞給hashimotoLight
,該函數(shù)能根據(jù)cache, hash, nonce
生成Digest和Result卿捎,然后校驗Digest是否匹配以及Result是否符合條件配紫。
hashimotoLight函數(shù)
hashimotoLight
使用cache, hash, nonce
生成Digest
和Result
。生成Digest和Result只需要部分的dataset數(shù)據(jù)午阵,而這些部分dataset數(shù)據(jù)時可以通過cache生成躺孝,因此也就不需要完整的dataset。它把generateDatasetItem
函數(shù)封裝成了獲取部分dataset數(shù)據(jù)的lookup函數(shù)底桂,然后傳遞給hashimoto
計算出Digest和Result植袍。
FAQ
- Q:每30000個塊使用同一個dataset,那可以提前挖出一些合法的Nonce籽懦?
A: 不行于个。提前挖去Nonce,意味著還不知道區(qū)塊頭的hash暮顺,因此無法生成合法的Nonce厅篓。 - Q:能否根據(jù)符合條件的哈希值,反推出Nonce呢捶码?
A:不行羽氮。因為哈希運算具有不可逆性,不能根據(jù)摘要反推出明文惫恼,同理根據(jù)哈希值也無法推出Nonce档押。