Vai al contenuto principale
Change page

Ethash

Ethash era l'algoritmo di minaggio della Prova di lavoro (PoW) di Ethereum. La Prova di lavoro (PoW) è stata ora completamente disattivata ed Ethereum è ora protetto utilizzando invece la Proof-of-Stake (PoS). Maggiori informazioni su The Merge, sulla Proof-of-Stake (PoS) e sullo staking. Questa pagina è di interesse storico!

Ethash è una versione modificata dell'algoritmo Dagger-Hashimoto. La Prova di lavoro (PoW) di Ethash è memory hard (opens in a new tab), il che si pensava rendesse l'algoritmo resistente agli ASIC. Alla fine sono stati sviluppati ASIC per Ethash, ma il minaggio tramite GPU è rimasto un'opzione praticabile fino alla disattivazione della Prova di lavoro (PoW). Ethash è ancora utilizzato per il minaggio di altre monete su altre reti basate sulla Prova di lavoro (PoW) diverse da Ethereum.

Come funziona Ethash?

La "memory hardness" (resistenza della memoria) si ottiene con un algoritmo di Prova di lavoro (PoW) che richiede la scelta di sottoinsiemi di una risorsa fissa dipendente dal nonce e dall'intestazione del blocco. Questa risorsa (della dimensione di alcuni gigabyte) è chiamata DAG. Il DAG viene modificato ogni 30000 blocchi, una finestra di circa 125 ore chiamata epoca (circa 5,2 giorni) e richiede un po' di tempo per essere generato. Poiché il DAG dipende solo dall'altezza del blocco, può essere pre-generato, ma in caso contrario il client deve attendere la fine di questo processo per produrre un blocco. Se i client non pre-generano e non memorizzano nella cache i DAG in anticipo, la rete potrebbe subire un massiccio ritardo dei blocchi a ogni transizione di epoca. Si noti che il DAG non deve essere generato per verificare la Prova di lavoro (PoW), consentendo essenzialmente la verifica con un basso utilizzo di CPU e poca memoria.

Il percorso generale seguito dall'algoritmo è il seguente:

  1. Esiste un seme (seed) che può essere calcolato per ogni blocco scansionando le intestazioni del blocco fino a quel punto.
  2. Dal seme, si può calcolare una cache pseudocasuale di 16 MB. I client leggeri memorizzano la cache.
  3. Dalla cache, possiamo generare un dataset di 1 GB, con la proprietà che ogni elemento nel dataset dipende solo da un piccolo numero di elementi della cache. I client completi e i minatori memorizzano il dataset. Il dataset cresce linearmente nel tempo.
  4. Il minaggio comporta il prelievo di porzioni casuali del dataset e il loro hashing congiunto. La verifica può essere eseguita con poca memoria utilizzando la cache per rigenerare i pezzi specifici del dataset di cui si ha bisogno, quindi è necessario memorizzare solo la cache.

Il grande dataset viene aggiornato una volta ogni 30000 blocchi, quindi la stragrande maggioranza dello sforzo di un minatore consisterà nel leggere il dataset, non nell'apportarvi modifiche.

Definizioni

Utilizziamo le seguenti definizioni:

L'uso di 'SHA3'

Lo sviluppo di Ethereum ha coinciso con lo sviluppo dello standard SHA3 e il processo di standardizzazione ha apportato una modifica tardiva al padding dell'algoritmo di hash finalizzato, in modo che gli hash "sha3_256" e "sha3_512" di Ethereum non siano hash sha3 standard, ma una variante spesso indicata come "Keccak-256" e "Keccak-512" in altri contesti. Si veda la discussione, ad es., qui (opens in a new tab), qui (opens in a new tab) o qui (opens in a new tab).

Si prega di tenerlo a mente poiché si fa riferimento agli hash "sha3" nella descrizione dell'algoritmo di seguito.

Parametri

I parametri per la cache e il dataset di Ethash dipendono dal numero del blocco. La dimensione della cache e la dimensione del dataset crescono entrambe linearmente; tuttavia, prendiamo sempre il numero primo più alto al di sotto della soglia di crescita lineare al fine di ridurre il rischio di regolarità accidentali che portano a comportamenti ciclici.

Le tabelle dei valori delle dimensioni del dataset e della cache sono fornite nell'appendice.

Generazione della cache

Ora, specifichiamo la funzione per produrre una cache:

Il processo di produzione della cache prevede prima il riempimento sequenziale di 32 MB di memoria, quindi l'esecuzione di due passaggi dell'algoritmo RandMemoHash di Sergio Demian Lerner da Strict Memory Hard Hashing Functions (2014) (opens in a new tab). L'output è un set di 524288 valori da 64 byte.

Funzione di aggregazione dei dati

Utilizziamo un algoritmo ispirato all'hash FNV (opens in a new tab) in alcuni casi come sostituto non associativo per lo XOR. Si noti che moltiplichiamo il numero primo per l'intero input a 32 bit, in contrasto con le specifiche FNV-1 che moltiplicano il numero primo per un byte (ottetto) alla volta.

FNV_PRIME = 0x01000193

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

Si prega di notare che, sebbene lo yellow paper specifichi fnv come v1*(FNV_PRIME ^ v2), tutte le implementazioni attuali utilizzano coerentemente la definizione di cui sopra.

Calcolo del dataset completo

Ogni elemento da 64 byte nel dataset completo da 1 GB viene calcolato come segue:

Essenzialmente, combiniamo i dati da 256 nodi della cache selezionati in modo pseudocasuale e ne eseguiamo l'hashing per calcolare il nodo del dataset. L'intero dataset viene quindi generato da:

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

Ciclo principale

Ora, specifichiamo il ciclo principale in stile "hashimoto", in cui aggreghiamo i dati dal dataset completo al fine di produrre il nostro valore finale per una particolare intestazione e nonce. Nel codice sottostante, header rappresenta l'hash SHA3-256 della rappresentazione RLP di un'intestazione del blocco troncata, ovvero di un'intestazione che esclude i campi mixHash e nonce. nonce sono gli otto byte di un intero senza segno a 64 bit in ordine big-endian. Quindi nonce[::-1] è la rappresentazione little-endian a otto byte di quel valore:

Essenzialmente, manteniamo un "mix" largo 128 byte e recuperiamo ripetutamente in modo sequenziale 128 byte dal dataset completo e utilizziamo la funzione fnv per combinarlo con il mix. Vengono utilizzati 128 byte di accesso sequenziale in modo che ogni round dell'algoritmo recuperi sempre un'intera pagina dalla RAM, riducendo al minimo i mancati riscontri nel translation lookaside buffer (TLB) che gli ASIC sarebbero teoricamente in grado di evitare.

Se l'output di questo algoritmo è inferiore all'obiettivo desiderato, allora il nonce è valido. Si noti che l'applicazione aggiuntiva di sha3_256 alla fine garantisce l'esistenza di un nonce intermedio che può essere fornito per dimostrare che è stata eseguita almeno una piccola quantità di lavoro; questa rapida verifica esterna della Prova di lavoro (PoW) può essere utilizzata per scopi anti-DDoS. Serve anche a fornire la garanzia statistica che il risultato sia un numero a 256 bit non distorto.

Minaggio

L'algoritmo di minaggio è definito come segue:

def mine(full_size, dataset, header, difficulty):
    # riempi di zeri il target per confrontarlo con l'hash sulla stessa cifra
    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

Definizione dell'hash del seme

Al fine di calcolare l'hash del seme che verrebbe utilizzato per il minaggio in cima a un dato blocco, utilizziamo il seguente algoritmo:

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

Si noti che per un minaggio e una verifica fluidi, consigliamo di pre-calcolare gli hash dei semi e i dataset futuri in un thread separato.

Letture consigliate

Conosci una risorsa della community che ti è stata utile? Modifica questa pagina e aggiungila!

Appendice

Il seguente codice dovrebbe essere anteposto se si è interessati a eseguire le specifiche Python di cui sopra come codice.

Dimensioni dei dati

Le seguenti tabelle di ricerca forniscono circa 2048 epoche tabulate di dimensioni dei dati e dimensioni della cache.