Przejdź do głównej treści
Change page

Ethash

Ethash był algorytmem wydobywczym Ethereum opartym na dowodzie pracy. Mechanizm dowodu pracy został już całkowicie wyłączony, a zamiast niego sieć Ethereum jest teraz zabezpieczona przy użyciu dowodu stawki. Przeczytaj więcej o Połączeniu, dowodzie stawki i stakingu. Ta strona ma jedynie znaczenie historyczne!

Ethash to zmodyfikowana wersja algorytmu Dagger-Hashimoto. Dowód pracy Ethash jest trudny pod względem pamięci (opens in a new tab), co, jak sądzono, miało uczynić algorytm odpornym na układy ASIC. Ostatecznie opracowano układy ASIC dla Ethash, ale wydobycie za pomocą GPU było nadal realną opcją, dopóki dowód pracy nie został wyłączony. Ethash jest nadal używany do wydobywania innych monet w innych sieciach opartych na dowodzie pracy, które nie są sieciami Ethereum.

Jak działa Ethash?

Trudność pod względem pamięci jest osiągana dzięki algorytmowi dowodu pracy, który wymaga wyboru podzbiorów stałego zasobu w zależności od wartości nonce i nagłówka bloku. Ten zasób (o rozmiarze kilku gigabajtów) nazywany jest DAG. DAG jest zmieniany co 30 000 bloków, co jest ~125-godzinnym oknem czasowym nazywanym epoką (około 5,2 dnia), a jego generowanie zajmuje trochę czasu. Ponieważ DAG zależy tylko od wysokości bloku, może on być wstępnie wygenerowany, ale jeśli nie jest, klient musi poczekać do końca tego procesu, aby wyprodukować blok. Jeśli klienci nie generują wstępnie i nie buforują DAG-ów z wyprzedzeniem, sieć może doświadczyć ogromnego opóźnienia bloku przy każdej zmianie epoki. Należy pamiętać, że DAG nie musi być generowany w celu weryfikacji dowodu pracy, co zasadniczo pozwala na weryfikację przy niskim użyciu procesora i małej ilości pamięci.

Ogólny schemat działania algorytmu jest następujący:

  1. Istnieje ziarno (seed), które można obliczyć dla każdego bloku, skanując nagłówki bloków aż do tego momentu.
  2. Z ziarna można obliczyć 16 MB pseudolosowej pamięci podręcznej. Lekcy klienci przechowują pamięć podręczną.
  3. Z pamięci podręcznej możemy wygenerować zbiór danych o wielkości 1 GB z tą właściwością, że każdy element w zbiorze danych zależy tylko od niewielkiej liczby elementów z pamięci podręcznej. Pełni klienci i górnicy przechowują zbiór danych. Zbiór danych rośnie liniowo w czasie.
  4. Wydobycie polega na pobieraniu losowych fragmentów zbioru danych i haszowaniu ich razem. Weryfikację można przeprowadzić przy niskim zużyciu pamięci, wykorzystując pamięć podręczną do regeneracji potrzebnych fragmentów zbioru danych, dzięki czemu wystarczy przechowywać tylko pamięć podręczną.

Duży zbiór danych jest aktualizowany raz na 30 000 bloków, więc zdecydowana większość wysiłku górnika polegać będzie na odczytywaniu zbioru danych, a nie na wprowadzaniu do niego zmian.

Definicje

Stosujemy następujące definicje:

Użycie 'SHA3'

Rozwój Ethereum zbiegł się w czasie z rozwojem standardu SHA3, a proces standaryzacji wprowadził późną zmianę w dopełnianiu sfinalizowanego algorytmu haszującego, tak że hasze Ethereum "sha3_256" i "sha3_512" nie są standardowymi haszami sha3, ale wariantem często określanym jako "Keccak-256" i "Keccak-512" w innych kontekstach. Zobacz dyskusję, np. tutaj (opens in a new tab), tutaj (opens in a new tab) lub tutaj (opens in a new tab).

Proszę o tym pamiętać, ponieważ hasze „sha3” są przywoływane w poniższym opisie algorytmu.

Parametry

Parametry pamięci podręcznej i zbioru danych Ethash zależą od numeru bloku. Rozmiar pamięci podręcznej i rozmiar zbioru danych rosną liniowo; jednak zawsze bierzemy największą liczbę pierwszą poniżej liniowo rosnącego progu, aby zmniejszyć ryzyko przypadkowych regularności prowadzących do zachowań cyklicznych.

Tabele wartości rozmiaru zbioru danych i rozmiaru pamięci podręcznej znajdują się w załączniku.

Generowanie pamięci podręcznej

Teraz określamy funkcję do tworzenia pamięci podręcznej:

Proces tworzenia pamięci podręcznej obejmuje najpierw sekwencyjne wypełnienie 32 MB pamięci, a następnie wykonanie dwóch przejść algorytmu RandMemoHash autorstwa Sergio Demiana Lernera z pracy Strict Memory Hard Hashing Functions (2014) (opens in a new tab). Wynikiem jest zbiór 524 288 64-bajtowych wartości.

Funkcja agregacji danych

W niektórych przypadkach używamy algorytmu inspirowanego haszem FNV (opens in a new tab) jako niełącznego substytutu dla XOR. Należy zauważyć, że mnożymy liczbę pierwszą przez pełne 32-bitowe wejście, w przeciwieństwie do specyfikacji FNV-1, która z kolei mnoży liczbę pierwszą przez jeden bajt (oktet).

FNV_PRIME = 0x01000193

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

Należy pamiętać, że chociaż Yellow Paper określa fnv jako v1*(FNV_PRIME ^ v2), wszystkie obecne implementacje konsekwentnie używają powyższej definicji.

Obliczanie pełnego zbioru danych

Każdy 64-bajtowy element w pełnym zbiorze danych o rozmiarze 1 GB jest obliczany w następujący sposób:

Zasadniczo łączymy dane z 256 pseudolosowo wybranych węzłów pamięci podręcznej i haszujemy je, aby obliczyć węzeł zbioru danych. Cały zbiór danych jest następnie generowany przez:

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

Pętla główna

Teraz określimy główną pętlę w stylu „hashimoto”, w której agregujemy dane z pełnego zbioru danych w celu wytworzenia naszej ostatecznej wartości dla konkretnego nagłówka i wartości nonce. W poniższym kodzie header reprezentuje hasz SHA3-256 reprezentacji RLP skróconego nagłówka bloku, czyli nagłówka z wyłączeniem pól mixHash oraz nonce. nonce to osiem bajtów 64-bitowej liczby całkowitej bez znaku w porządku big-endian. Zatem nonce[::-1] jest ośmiobajtową reprezentacją tej wartości w porządku little-endian:

Zasadniczo utrzymujemy „miks” o szerokości 128 bajtów i wielokrotnie sekwencyjnie pobieramy 128 bajtów z pełnego zbioru danych, używając funkcji fnv do połączenia ich z miksem. 128 bajtów dostępu sekwencyjnego jest używane, aby każda runda algorytmu zawsze pobierała pełną stronę z pamięci RAM, minimalizując chybienia w buforze translacji adresów (TLB), których układy ASIC teoretycznie mogłyby uniknąć.

Jeśli wynik tego algorytmu jest poniżej pożądanego celu, to wartość nonce jest prawidłowa. Należy pamiętać, że dodatkowe zastosowanie sha3_256 na końcu zapewnia istnienie pośredniej wartości nonce, którą można podać w celu udowodnienia, że wykonano co najmniej niewielką ilość pracy; ta szybka zewnętrzna weryfikacja PoW może być używana do celów ochrony przed atakami DDoS. Służy to również zapewnieniu statystycznej pewności, że wynik jest nieobciążoną, 256-bitową liczbą.

Wydobycie

Algorytm wydobycia jest zdefiniowany w następujący sposób:

def mine(full_size, dataset, header, difficulty):
    # dopełnij cel zerami, aby porównać z haszem na tej samej cyfrze
    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

Definiowanie haszu ziarna

Aby obliczyć hasz ziarna, który byłby używany do wydobywania na danym bloku, używamy następującego algorytmu:

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

Należy pamiętać, że w celu płynnego wydobywania i weryfikacji zalecamy wstępne obliczanie przyszłych haszy ziaren i zbiorów danych w osobnym wątku.

Dalsza lektura

Znasz jakieś zasoby społeczności, które Ci pomogły? Edytuj tę stronę i dodaj je!

Dodatek

Poniższy kod należy dołączyć na początku, jeśli chcesz uruchomić powyższą specyfikację Pythona jako kod.

Rozmiary danych

Poniższe tabele przeglądowe zawierają dane dotyczące rozmiarów danych i pamięci podręcznej dla około 2048 epok.

Ostatnia aktualizacja strony: 15 kwietnia 2026

Czy ten artykuł był pomocny?