跳至主要內容
Change page

Ethash

頁面最後更新: 2026年4月15日

Ethash 是以太坊的工作量證明挖礦演算法。 工作量證明現在已經被完全關閉,取而代之的是,以太坊現在使用權益證明來確保安全。 閱讀更多關於合併權益證明質押的資訊。 此頁面僅為滿足對歷史的興趣!

Ethash 是 Dagger-Hashimoto 演算法的修改版本。 Ethash 工作量證明是記憶體困難型 (opens in a new tab),一般認為這讓此演算法具備抗 ASIC 的能力。 Ethash 專用積體電路最終被開發出來,但圖形處理單元挖礦仍然是一個可行的選擇,直至工作量證明被關閉。 Ethash 仍在其他非以太坊工作量證明網路上用於挖掘其他代幣。

Ethash 是如何運作的?

記憶體密集性是透過工作量證明演算法實現的,需要根據隨機數和區塊頭選擇固定資源子集。 此資源(大小為數 GB)稱為有向無環圖。 有向無環圖每 30000 個區塊更改一次(大約 125 小時的窗口,稱為一個時期(約 5.2 天)),並需要一段時間才能產生。 由於有向無環圖僅依賴區塊高度,因此可以預先生成,但如果沒有,則用戶端需要等到此過程結束後才能生成區塊。 如果用戶端沒有提前預先產生和快取有向無環圖,網路可能會在每個時期過渡時遭遇嚴重的區塊延遲。 請注意,不用產生有向無環圖即可驗證工作量證明,這在本質上允許使用低階中央處理器和小記憶體進行驗證。

此演算法所採取的一般路線如下:

  1. 每個區塊都有一個種子,可藉由掃描該點之前的區塊頭來計算得出。
  2. 從種子可以計算出 16 MB 的偽隨機快取。 由輕量用戶端儲存該快取。
  3. 我們可以從快取中產生一個 1 GB 的資料集,此資料集的特性是,其中每個項目都只依賴快取中的少數項目。 由全用戶端和礦工儲存該資料集。 該資料集隨著時間的流逝而呈線性增長。
  4. 挖礦需要取得該資料集的隨機片段並將它們雜湊在一起。 可以透過使用快取來重新產生你所需要的資料集中的特定片段,以較少的記憶體進行驗證,這樣你就只需要儲存快取。

每隔 30,000 個區塊更新一次大資料集,因此,礦工的絕大部分工作都是讀取資料集,而不是對其進行修改。

定義

我們採用以下定義:

使用「SHA3」

以太坊的開發恰逢 SHA3 標準的製定,標準流程對最終確定的雜湊值演算法的填充做了後期改動,使得以太坊的「sha3_256」和「sha3_512」雜湊值不是標準的 sha3 雜湊值,而是在其他情況下常被稱為「Keccak-256」和「Keccak-512」的變體。 請參閱討論,例如:此處 (opens in a new tab)此處 (opens in a new tab)此處 (opens in a new tab)

請記住這一點,因為下面的演算法描述中提到了「sha3」雜湊值。

參數

Ethash 快取和資料集的參數取決於區塊號。 快取大小和資料集大小都呈線性增長;然而,我們總是取低於線性增長閾值的最大素數,以降低意外的規律導致循環行為的風險。

附錄中提供了資料集和快取大小值表。

快取生成

現在,我們來指定產生快取的函式:

快取生成過程首先會循序填滿 32 MB 的記憶體,然後執行兩輪 Sergio Demian Lerner 的 RandMemoHash 演算法,此演算法出自 Strict Memory Hard Hashing Functions (2014) (opens in a new tab)。 輸出一組 524288 個 64 字節位元組值。

資料匯總函式

在某些情況下,我們使用一種受 FNV 雜湊 (opens in a new tab)啟發的演算法,作為 XOR 的非關聯性替代品。 請注意,我們使用全 32 位元輸入乘以素數,與之相對地,FNV-1 規範以單字節位元組(8 位元組)依序乘以素數。

FNV_PRIME = 0x01000193

def fnv(v1, v2):
    return ((v1 * FNV_PRIME) ^ v2) % 2**32

請注意,即使黃皮書指出 fnv 為 v1*(FNV_PRIME ^ v2),所有目前實作始終採用上述定義。

完整資料集計算

整個 1 GB 資料集中的每個 64 字節位元組項目的計算方法如下:

基本上,我們會將 256 個偽隨機選取的快取節點的資料合併起來求雜湊值,以計算資料集節點。 然後產生整個資料集:

def calc_dataset(full_size, cache):
    return [calc_dataset_item(cache, i) for i in range(full_size // HASH_BYTES)]

主迴圈

現在,我們指定了類似「hashimoto」的主要循環。在此循環中,我們匯總了整個資料集的資料,以產生特定區塊頭和隨機數的最終值。 在下方的程式碼中,header 代表一個_被截斷_區塊標頭的 RLP 表示的 SHA3-256 雜湊值,也就是說,這是一個排除了 mixHashnonce 欄位的標頭。 nonce 是 64 位元無正負號整數的八個位元組,採大端序 (big-endian) 排列。 所以 nonce[::-1] 是該值的八位元組小端序 (little-endian) 表示法:

基本上,我們維護一個 128 位元組寬的 "mix",並從完整資料集中重複循序擷取 128 位元組,然後使用 fnv 函式將其與 mix 結合。 使用 128 字節位元組的順序存取,以便每輪演算法總是能從隨機存取記憶體獲取完整的頁面,從而盡量減少轉譯後備緩衝區的失誤,而專用積體電路在理論上能夠避免這些失誤。

如果此演算法的輸出低於所需目標,即證明隨機數是有效的。 請注意,結尾額外應用 sha3_256 可確保存在一個中介 nonce,此 nonce 可用來證明至少已完成少量工作;這種快速的外部 PoW 驗證可用於反 DDoS 目的。 它還能提供統計保證,確保結果是一個無偏的 256 位元數。

挖礦

挖礦演算法定義如下:

def mine(full_size, dataset, header, difficulty):
    # 將目標補零,以便與雜湊值比較
    target = zpad(encode_int(2**256 // difficulty), 64)[::-1]
    from random import randint
    nonce = randint(0, 2**64)
    while hashimoto_full(full_size, dataset, header, nonce) > target:
        nonce = (nonce + 1) % 2**64
    return nonce

定義種子雜湊值

為了計算用於在給定區塊上挖礦的種子雜湊值,我們使用以下演算法:

 def get_seedhash(block):
     s = '\x00' * 32
     for i in range(block.number // EPOCH_LENGTH):
         s = serialize_hash(sha3_256(s))
     return s

請注意,為了順利挖礦和驗證,我們建議在單獨執行緒中預先計算未來的種子雜湊值和資料集。

延伸閱讀

知道一個曾經幫助你學習更多社區或社團資源? 歡迎在本頁自由編輯或添加內容!

附錄

如果你有興趣將上述 python 規範作為程式碼運行,應在前面添加以下程式碼。

資料大小

以下查找表提供了約 2048 個製錶格式的資料大小和快取大小時期。

這篇文章對您有幫助嗎?